sklearn LabelBinarizer returns vector when there are 2 classes

青春壹個敷衍的年華 提交于 2021-02-17 12:45:16

问题


The following code:

from sklearn.preprocessing import LabelBinarizer
lb = LabelBinarizer()
lb.fit_transform(['yes', 'no', 'no', 'yes'])

returns:

array([[1],
       [0],
       [0],
       [1]])

However, I would like for there to be one column per class:

array([[1, 0],
       [0, 1],
       [0, 1],
       [1, 0]])

(I need the data in this format so I can give it to a neural network that uses the softmax function at the output layer)

When there are more than 2 classes, LabelBinarizer behaves as desired:

from sklearn.preprocessing import LabelBinarizer
lb = LabelBinarizer()
lb.fit_transform(['yes', 'no', 'no', 'yes', 'maybe'])

returns

array([[0, 0, 1],
       [0, 1, 0],
       [0, 1, 0],
       [0, 0, 1],
       [1, 0, 0]])

Above, there is 1 column per class.

Is there any simple way to achieve the same (1 column per class) when there are 2 classes?

Edit: Based on yangjie's answer I wrote a class to wrap LabelBinarizer to produce the desired behavior described above: http://pastebin.com/UEL2dP62

import numpy as np
from sklearn.preprocessing import LabelBinarizer


class LabelBinarizer2:

    def __init__(self):
        self.lb = LabelBinarizer()

    def fit(self, X):
        # Convert X to array
        X = np.array(X)
        # Fit X using the LabelBinarizer object
        self.lb.fit(X)
        # Save the classes
        self.classes_ = self.lb.classes_

    def fit_transform(self, X):
        # Convert X to array
        X = np.array(X)
        # Fit + transform X using the LabelBinarizer object
        Xlb = self.lb.fit_transform(X)
        # Save the classes
        self.classes_ = self.lb.classes_
        if len(self.classes_) == 2:
            Xlb = np.hstack((Xlb, 1 - Xlb))
        return Xlb

    def transform(self, X):
        # Convert X to array
        X = np.array(X)
        # Transform X using the LabelBinarizer object
        Xlb = self.lb.transform(X)
        if len(self.classes_) == 2:
            Xlb = np.hstack((Xlb, 1 - Xlb))
        return Xlb

    def inverse_transform(self, Xlb):
        # Convert Xlb to array
        Xlb = np.array(Xlb)
        if len(self.classes_) == 2:
            X = self.lb.inverse_transform(Xlb[:, 0])
        else:
            X = self.lb.inverse_transform(Xlb)
        return X

Edit 2: It turns out yangjie has also written a new version of LabelBinarizer, awesome!


回答1:


I think there is no direct way to do it especially if you want to have inverse_transform.

But you can use numpy to construct the label easily

In [18]: import numpy as np

In [19]: from sklearn.preprocessing import LabelBinarizer

In [20]: lb = LabelBinarizer()

In [21]: label = lb.fit_transform(['yes', 'no', 'no', 'yes'])

In [22]: label = np.hstack((label, 1 - label))

In [23]: label
Out[23]:
array([[1, 0],
       [0, 1],
       [0, 1],
       [1, 0]])

Then you can use inverse_transform by slicing the first column

In [24]: lb.inverse_transform(label[:, 0])
Out[24]:
array(['yes', 'no', 'no', 'yes'],
      dtype='<U3')

Based on the above solution, you can write a class that inherits LabelBinarizer, which makes the operations and results consistent for both binary and multiclass case.

from sklearn.preprocessing import LabelBinarizer
import numpy as np

class MyLabelBinarizer(LabelBinarizer):
    def transform(self, y):
        Y = super().transform(y)
        if self.y_type_ == 'binary':
            return np.hstack((Y, 1-Y))
        else:
            return Y

    def inverse_transform(self, Y, threshold=None):
        if self.y_type_ == 'binary':
            return super().inverse_transform(Y[:, 0], threshold)
        else:
            return super().inverse_transform(Y, threshold)

Then

lb = MyLabelBinarizer()
label1 = lb.fit_transform(['yes', 'no', 'no', 'yes'])
print(label1)
print(lb.inverse_transform(label1))
label2 = lb.fit_transform(['yes', 'no', 'no', 'yes', 'maybe'])
print(label2)
print(lb.inverse_transform(label2))

gives

[[1 0]
 [0 1]
 [0 1]
 [1 0]]
['yes' 'no' 'no' 'yes']
[[0 0 1]
 [0 1 0]
 [0 1 0]
 [0 0 1]
 [1 0 0]]
['yes' 'no' 'no' 'yes' 'maybe']



回答2:


this should do it

labels = ['yes', 'no', 'no', 'yes']
np.array([[1,0] if l=='yes' else [0,1] for l in labels])



回答3:


After so many years, now there is a nice elegant solution. The MultiLabelBinirazer.

The difference with LabelBinarizer is that treats all features as multi-class.

In scikit-learn 0.18 it cannot handle unseen values.

In scikit-learn 0.20 it can handle unseen values and assign them [0,0,...,0,0] which fixed recently.



来源:https://stackoverflow.com/questions/31947140/sklearn-labelbinarizer-returns-vector-when-there-are-2-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!