scikit-learn: одна горячая кодировка строковых категориальных функций
Я пытаюсь выполнить горячее кодирование тривиального набора данных.
data = [['a', 'dog', 'red']
['b', 'cat', 'green']]
Каков наилучший способ предварительной обработки этих данных с помощью Scikit-Learn?
При первом инстинкте вы посмотрите на OneHotEncoder Scikit-Learn. Но один горячий кодировщик не поддерживает строки как функции; это только дискретизирует целые числа.
Тогда вы должны использовать LabelEncoder, который будет кодировать строки в целые числа. Но затем вы должны применить кодировщик меток к каждому из столбцов и сохранить каждый из этих кодировщиков меток (а также столбцы, к которым они были применены). И это кажется очень неуклюжим.
Итак, как лучше всего это сделать в Scikit-Learn?
Пожалуйста, не предлагайте pandas.get_dummies. Это то, что я обычно использую в настоящее время для одного горячего кодирования. Тем не менее, он ограничен в том, что вы не можете кодировать свой набор тренировок / тестов отдельно.
3 ответа
Для вашего сведения, это скоро будет в производстве в sklearn: см. https://github.com/scikit-learn/scikit-learn/pull/9151
In [30]: cat = CategoricalEncoder()
In [31]: X = np.array([['a', 'b', 'a', 'c'], [0, 1, 0, 1]], dtype=object).T
In [32]: cat.fit_transform(X).toarray()
Out[32]:
array([[ 1., 0., 0., 1., 0.],
[ 0., 1., 0., 0., 1.],
[ 1., 0., 0., 1., 0.],
[ 0., 0., 1., 0., 1.]])
Если вы установите ветку master, вы сможете это сделать.
Другой способ сделать это - использовать category_encoders.
Вот пример:
% pip install category_encoders
import category_encoders as ce
le = ce.OneHotEncoder(return_df=False, impute_missing=False, handle_unknown="ignore")
X = np.array([['a', 'dog', 'red'], ['b', 'cat', 'green']])
le.fit_transform(X)
array([[1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1]])
Изменить: он уже включен master
ветка.
Изменить 2: Это в sklearn==0.20.dev0
Очень хороший вопрос
Однако, в некотором смысле, это частный случай чего-то, что встречается (по крайней мере, для меня) довольно часто - учитывая sklearn
этапы, применимые к подмножествам X
матрица, я хотел бы применить (возможно, несколько), учитывая всю матрицу. Здесь, например, у вас есть этап, который должен выполняться для одного столбца, и вы хотите применить его трижды - один раз для каждого столбца.
Это классический случай использования Composite Design Pattern.
Вот (эскиз) многоразового этапа, который принимает словарь, отображающий индекс столбца в преобразование, чтобы применить к нему:
class ColumnApplier(object):
def __init__(self, column_stages):
self._column_stages = column_stages
def fit(self, X, y):
for i, k in self._column_stages.items():
k.fit(X[:, i])
return self
def transform(self, X):
X = X.copy()
for i, k in self._column_stages.items():
X[:, i] = k.transform(X[:, i])
return X
Теперь, чтобы использовать его в этом контексте, начиная с
X = np.array([['a', 'dog', 'red'], ['b', 'cat', 'green']])
y = np.array([1, 2])
X
вы просто используете его для сопоставления каждого индекса столбца с преобразованием, которое вы хотите:
multi_encoder = \
ColumnApplier(dict([(i, preprocessing.LabelEncoder()) for i in range(3)]))
multi_encoder.fit(X, None).transform(X)
Как только вы разработаете такой этап (я не могу опубликовать тот, который я использую), вы можете использовать его снова и снова для различных настроек.
Я сталкивался с этой проблемой много раз и нашел решение в этой книге на его странице 100:
Мы можем применить оба преобразования (от текстовых категорий к целочисленным категориям, затем от целочисленных категорий к горячим векторам) в одном кадре, используя класс LabelBinarizer:
и пример кода здесь:
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()
housing_cat_1hot = encoder.fit_transform(data)
housing_cat_1hot
и как результат: обратите внимание, что по умолчанию возвращается массив NumPy. Вместо этого вы можете получить разреженную матрицу, передав sparse_output=True конструктору LabelBinarizer.
И вы можете узнать больше о LabelBinarizer, здесь, в официальной документации sklearn