TL;DR:对于一个简单的方法,pd.factorize。尽管对于使用通常的 scikit-learn fit/transform 方法 OrderedLabelEncoder 的方法进行了定义,但它只是重写了两个基类的方法以获得编码,其中代码按类的出现顺序排序。
object dtype 列中的类在LabelEncoder 中按字典顺序排序,这导致生成的代码显示为无序。这可以在_encode_python 中看到,它在fit 方法中被调用。其中,当dtype 列是object 时,classes 变量(然后用于映射值)通过采用set 来定义。一个明显的例子,可能是(复制_encode_python 中所做的):
df = pd.DataFrame([[1,'C'],[1,'C'],[1,'B'],[2,'A']], columns=['id','element'])
values = df.element.to_numpy()
# array(['C', 'C', 'B', 'A'], dtype=object)
uniques = sorted(set(values))
uniques = np.array(uniques, dtype=values.dtype)
table = {val: i for i, val in enumerate(uniques)}
print(table)
{'A': 0, 'B': 1, 'C': 2}
生成的set 用于定义一个查找表,该表将确定特征的顺序。
因此,在这种情况下,我们会得到:
ole = LabelEncoder()
ole.fit_transform(df.element)
# array([2, 2, 1, 0])
对于一个简单的替代方案,您有 pd.factorize,它将保持顺序:
df['element'] = pd.factorize(df.element)[0]
尽管如果您需要一个具有通常 scikit-learn fit/transform 方法的类,我们可以重新定义定义类的特定函数,并提出一个保持外观顺序的等价物。一种简单的方法是,使用uniques = list(dict.fromkeys(values)) 将列值设置为字典键(维护 Python >3.7 的插入顺序):
def ordered_encode_python(values, uniques=None, encode=False):
# only used in _encode below, see docstring there for details
if uniques is None:
uniques = list(dict.fromkeys(values))
uniques = np.array(uniques, dtype=values.dtype)
if encode:
table = {val: i for i, val in enumerate(uniques)}
try:
encoded = np.array([table[v] for v in values])
except KeyError as e:
raise ValueError("y contains previously unseen labels: %s"
% str(e))
return uniques, encoded
else:
return uniques
那么我们可以从LabelEncoder继承并将OrderedLabelEncoder定义为:
from sklearn.preprocessing import LabelEncoder
from sklearn.utils.validation import column_or_1d
class OrderedLabelEncoder(LabelEncoder):
def fit(self, y):
y = column_or_1d(y, warn=True)
self.classes_ = ordered_encode_python(y)
def fit_transform(self, y):
y = column_or_1d(y, warn=True)
self.classes_, y = ordered_encode_python(y, encode=True)
return y
然后可以像LabelEncoder 一样继续,例如:
ole = OrderedLabelEncoder()
ole.fit(df.element)
ole.classes_
# array(['C', 'B', 'A'], dtype=object)
ole.transform(df.element)
# array([0, 0, 1, 2])
ole.inverse_transform(np.array([0, 0, 1, 2]))
# array(['C', 'C', 'B', 'A'], dtype=object)
或者我们也可以拨打fit_transform:
ole.fit_transform(df.element)
# array([0, 0, 1, 2])