Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Category Encoders: Catalog & Experiments (Part 1)

We shall explore 24 encoders from 4 libraries:

libraryone-hot encodersother simple encoderscontrast encoderstarget/Bayesian encoders
sklearn.preprocessingOneHotEncoderLabelEncoder
OrdinalEncoder
LabelBinarizer
category_encodersOneHotEncoderOrdinalEncoder
BinaryEncoder
BaseNEncoder
CountEncoder
HashingEncoder
HelmertEncoder
SumEncoder
BackwardDifferenceEncoder
PolynomialEncoder
TargetEncoder
MEstimateEncoder
WOEEncoder
JamesSteinEncoder
LeaveOneOutEncoder
CatBoostEncoder
GLMMEncoder
pandasget_dummiesfactorize
keras.utilsto_categorical

Encoders map the original categories (often dtype=string) to a set of representing values (often dtype=int for simple encoders; dtype=float for target encoders). This notebook walks through a tour of the encoders listed in the table, exploring each non-target encoder one by one, producing a comparison table at the end. Target encoders shall be explored in detail in a separate notebook.



When to use which encoder to solve what problems? There is a good guide here: [Encode Smarter: How to Easily Integrate Categorical Encoding into Your Machine Learning Pipeline](https://innovation.alteryx.com/encode-smarter).

Loading...

Is encoding optional?

Not always. Some packages can’t digest string-type data without encoding. ‘Donkey’, ‘horse’ and ‘mule’, for instance, would not work whereas 0, 1 and 2 would.

Even when the package can digest data without encoding, they sometimes learn encoded data better.

Encoder types: a broad-stroke scan

We’ve got 2 dozen encoders here. Let’s take an overview by trying to group them into families according to observable behaviors.

True <class 'category_encoders.ordinal.OrdinalEncoder'>
True <class 'category_encoders.helmert.HelmertEncoder'>
True <class 'category_encoders.sum_coding.SumEncoder'>
True <class 'category_encoders.one_hot.OneHotEncoder'>
True <class 'category_encoders.binary.BinaryEncoder'>
True <class 'category_encoders.basen.BaseNEncoder'>
True <class 'category_encoders.count.CountEncoder'>
True <class 'category_encoders.backward_difference.BackwardDifferenceEncoder'>
CPU times: user 3min 48s, sys: 2min 49s, total: 6min 38s
Wall time: 6min 36s
fit_transform() missing argument: y
See, told ya it was going to break: <class 'category_encoders.target_encoder.TargetEncoder'>
fit_transform() missing argument: y
See, told ya it was going to break: <class 'category_encoders.m_estimate.MEstimateEncoder'>
fit_transform() missing argument: y
See, told ya it was going to break: <class 'category_encoders.woe.WOEEncoder'>
fit_transform() missing argument: y
See, told ya it was going to break: <class 'category_encoders.james_stein.JamesSteinEncoder'>
fit_transform() missing argument: y
See, told ya it was going to break: <class 'category_encoders.leave_one_out.LeaveOneOutEncoder'>
fit_transform() missing argument: y
See, told ya it was going to break: <class 'category_encoders.cat_boost.CatBoostEncoder'>
fit_transform() missing argument: y
See, told ya it was going to break: <class 'category_encoders.glmm.GLMMEncoder'>
<class 'category_encoders.helmert.HelmertEncoder'>
<class 'category_encoders.sum_coding.SumEncoder'>
<class 'category_encoders.backward_difference.BackwardDifferenceEncoder'>
<class 'category_encoders.polynomial.PolynomialEncoder'>

One-to-one simple, target-independent encoders

(299, array(['LO', 'HJ', 'DJ', 'KV', 'DP', 'GE', 'HQ', 'HC', 'EK', 'GS', 'HG', 'BY', 'HX', 'JK', 'FJ', 'LM', 'HK', 'MD', 'IG', 'JG', 'AN', 'AD', 'MC', 'KW', 'CK', 'LF', 'CS', 'GK', 'DC', 'LB', 'FM', 'IH', 'LN', 'IK', 'DF', 'IB', 'CB', 'LY', 'JW', 'FI', 'CR', 'IE', 'LE', 'HB', 'HV', 'LG', 'BG', 'KP', 'LI', 'HL', 'BF', 'LU', 'O', 'GI', 'DQ', 'IR', 'DV', 'HA', 'KB', 'FP', 'AT', 'IF', 'HN', 'GC', 'C', 'KC', 'G', 'JA', 'CU', 'BC', 'AB', 'KF', 'MB', 'HE', 'BL', 'FQ', 'IA', 'MJ', 'FO', 'V', 'JT', 'AU', 'IO', 'GQ', 'CC', 'JR', 'BM', 'HH', 'AV', 'GT', 'I', 'IU', 'JN', 'EV', 'MV', 'EQ', 'LW', 'FN', 'IT', 'AA', 'DK', 'IJ', 'GU', 'P', 'JH', 'CM', 'GA', 'R', 'LX', 'IX', 'DY', 'D', 'FL', 'CP', 'GL', 'DI', 'CD', 'IV', 'FS', 'FR', 'J', 'MP', 'MH', 'EL', 'JD', 'AP', 'AE', 'F', 'LC', 'BP', 'BI', 'MF', 'DO', 'MG', 'MT', 'LD', 'CW', 'KS', 'BV', 'JV', 'BB', 'AM', 'KX', 'FK', 'AH', 'LV', 'W', 'DU', 'FB', 'JX', 'KA', 'CO', 'AR', 'KR', 'JI', 'T', 'JP', 'LQ', 'FX', 'FD', 'EY', 'Y', 'JO', 'EC', 'HM', 'AC', 'DW', 'HU', 'FH', 'AY', 'AL', 'GD', 'GB', 'DS', 'FT', 'KH', 'CG', 'JB', 'E', 'CN', 'BT', 'X', 'BX', 'HW', 'EI', 'ID', 'KT', 'GR', 'L', 'KG', 'EA', 'HO', 'GX', 'K', 'AS', 'DM', 'AK', 'FC', 'MS', 'HR', 'EU', 'ES', 'JY', 'HP', 'KL', 'FE', 'CY', 'EO', 'KJ', 'CJ', 'CI', 'JL', 'IC', 'S', 'DH', 'GN', 'BS', 'AG', 'M', 'EW', 'FA', 'LJ', 'GJ', 'KQ', 'HF', 'MR', 'BQ', 'ED', 'FG', 'LL', 'EG', 'HY', 'EH', 'GW', 'BD', 'IQ', 'Q', 'DA', 'DD', 'GM', 'KN', 'MQ', 'GY', 'KD', 'JJ', 'CL', 'IY', 'KU', 'CT', 'KK', 'DN', 'BO', 'IP', 'LH', 'IM', 'DE', 'ME', 'EE', 'LT', 'LR', 'MI', 'CF', 'DR', 'EB', 'KI', 'DX', 'DL', 'MW', 'FF', 'EF', 'EP', 'MU', 'MA', 'GG', 'CQ', 'DT', 'FV', 'CH', 'AF', 'AJ', 'IN', 'JC', 'EN', 'JU', 'JE', 'ML', 'AW', 'HI', 'MO', 'GF', 'MK', 'GH', 'FW', 'GV', 'JF', 'BA', 'LK', 'IL', 'CX'], dtype=object))
CPU times: user 29.3 s, sys: 8.26 s, total: 37.5 s
Wall time: 37.5 s
Loading...

1. Label & Ordinal encoders

From the table we find the first 3 rows:

  • sklearn.preprocessing._label.LabelEncoder

  • sklearn.preprocessing._encoders.OrdinalEncoder

  • category_encoders.ordinal.OrdinalEncoder

rather similar to each other:

  • they all output 299 unique numbers, where 299 is the cardinality of the original input;

  • they all output a single column;

  • they basically do one-to-one mapping of the original input;

  • they run quickly compared to the rest.

1.1 LabelEncoder vs OrdinalEncoder

  • LabelEncoder encodes one variable at a time; meant for encoding target labels (as in classification problems).

  • OrdinalEncoder encodes multiple variables/columns at a time; meant for encoding features (plural).

Let’s see that in action:

name 'LabelEncoder' is not defined
See, told ya it was going to break.

1.2 pandas does label encoding too

Loading...
could not broadcast input array from shape (300000,2) into shape (300000)
See, told ya it was going to break.

2. One-hot encoders

2.1 by scikit-learn and catagory-encoders

Loading...

Compared to label and ordinal encoders, we find that with one-hot encoders:

  • nunique dropped from 299 to 2;

  • the number of columns increased from 1 to 299.

Let’s see how a one-hot encoder maps input to output:

Loading...

One-hot encoding is thus name because for each row there is strictly one 1; all other columns must be zero. Let’s do a quick check:

<Figure size 432x288 with 1 Axes>

2.2 by pandas

Loading...

2.3 by keras

But with numeric input only. cat10 is string, not numeric. We would need to first convert from string to numeric.

invalid literal for int() with base 10: 'LO'
See, told ya it was going to break.
Loading...

Warning

keras.utils.to_categorical doesn’t work with negative input.

5
4
3
-2 [0. 1. 0.]
-1 [0. 0. 1.]
0 [1. 0. 0.]
1 [0. 1. 0.]
2 [0. 0. 1.]

3. Binary & Base-N Encoders

Base-N encoding is the superset of

  • binary encoding (N=2);

  • one-hot encoding (N=1). By default category_encoders.BaseNEncoder takes N=2; the output is there for identical to category_encoders.BinaryEncoder:

Loading...
True
But do we really need 10 columns?

2^8, 2^9 = 256, 512 so we should only need 9 columns to binary-encode 299 categories.

cat10_0 [0] cat10_1 [0, 1] cat10_2 [0, 1] cat10_3 [0, 1] cat10_4 [0, 1] cat10_5 [0, 1] cat10_6 [0, 1] cat10_7 [0, 1] cat10_8 [0, 1] cat10_9 [0, 1] dtype: object
Loading...

4. Count Encoder

Loading...
([-2, -1, 0, 1, 2], cat10 id 499996 3011 499997 565 499999 5917)
3011
565
5917

5. Contrast Encoders

These are contrast encoders characterised by the presence of an intercept in the output.

5.1 Helmert Encoder

Loading...

5.2 Sum Encoder

Loading...
intercept 299.0 cat10_0 0.0 cat10_1 0.0 cat10_2 0.0 cat10_3 0.0 ... cat10_293 0.0 cat10_294 0.0 cat10_295 0.0 cat10_296 0.0 cat10_297 0.0 Length: 299, dtype: float64
intercept 299.0 dtype: float64

5.3 Backward-Difference Encoder

Loading...

Grand summary

Loading...

This notebook is getting a little long. We’ve covered simple, one-to-one mapping encoders. Let’s do target encoders in another notebook!