scikit-learn 转换器根据用户提供的切点对数据进行分类
scikit-learn transformer that bins data based on user supplied cut points
我正在尝试在 scikit-learn 管道中包含一个转换器,该转换器将根据我自己提供的切点将连续数据列合并为 4 个值。 KBinsDiscretizer 的当前参数不起作用,主要是因为 strategy
参数仅接受 {‘uniform’, ‘quantile’, ‘kmeans’}
。
pandas 中已经有 cut()
函数,所以我想我需要创建一个自定义转换器来包装 cut()
函数行为。
期望的行为(不是实际的)
X = [[-2, -1, -0.5, 0, 0.5, 1, 2]]
est = Discretizer(bins=[-float("inf"), -1.0, 0.0, 1.0, float("inf")],
encode='ordinal')
est.fit(X)
est.transform(X)
# >>> array([[0., 0., 1., 1., 2., 2., 3.]])
上面的结果假定 bins 包括最右边的边缘和最低的边缘。像这样 pd.cut()
命令将提供:
import pandas as pd
import numpy as np
pd.cut(np.array([-2, -1, -0.5, 0, 0.5, 1, 2]),
[-float("inf"), -1.0, 0.0, 1.0, float("inf")],
labels=False, right=True, include_lowest=True)
# >>> array([0, 0, 1, 1, 2, 2, 3])
作为自定义转换器,这似乎对我有用。 scikit-learn 需要数字数组,所以我不确定您是否可以实现 pd.cut()
的功能,即 return 标签。出于这个原因,我在下面的实现中将其硬编码为 False
。
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
class CutTransformer(BaseEstimator, TransformerMixin):
def __init__(self, bins, right=True, retbins=False,
precision=3, include_lowest=False,
duplicates='raise'):
self.bins = bins
self.right = right
self.labels = False
self.retbins = retbins
self.precision = precision
self.include_lowest = include_lowest
self.duplicates = duplicates
def fit(self, X, y=None):
return self
def transform(self, X, y=None):
assert isinstance(X, pd.DataFrame)
for jj in range(X.shape[1]):
X.iloc[:, jj] = pd.cut(x=X.iloc[:, jj].values, **self.__dict__)
return X
一个例子
df = pd.DataFrame(data={'rand': np.random.rand(5)})
df
rand
0 0.030653
1 0.542533
2 0.159646
3 0.963112
4 0.539530
ct = CutTransformer(bins=np.linspace(0, 1, 5))
ct.transform(df)
rand
0 0
1 2
2 0
3 3
4 2
自定义转换器的替代方法会产生更多开销,它是使用 FunctionTransformer()
方法,该方法适用于无状态操作,例如预定义 bin 的情况。
import pandas as pd
from sklearn.preprocessing import FunctionTransformer
from sklearn.pipeline import make_pipeline
def ftransformer_cut(X, **kwargs):
if 'labels' not in kwargs:
kwargs['labels'] = False
assert isinstance(X, np.ndarray)
assert kwargs['labels'] == False
for jj in range(X.shape[1]):
X[:, jj] = pd.cut(x=X[:, jj], **kwargs)
return X
pipeline = make_pipeline(
FunctionTransformer(ftransformer_cut,
kw_args={'bins': np.linspace(0, 1, 5)})
)
df = pd.DataFrame(data={'rand': np.random.rand(5)})
rand
0 0.823234
1 0.336883
2 0.713595
3 0.408184
4 0.038
pipeline.transform(df)
array([[3.],
[1.],
[2.],
[1.],
[0.]])
唯一的问题是您只是转换传入数据,而不是在拟合阶段从训练中学习 bin 并在转换阶段使用该信息。理想情况下,您应该在拟合期间学习 bin 边缘并在变换期间分配 bin。
我正在尝试在 scikit-learn 管道中包含一个转换器,该转换器将根据我自己提供的切点将连续数据列合并为 4 个值。 KBinsDiscretizer 的当前参数不起作用,主要是因为 strategy
参数仅接受 {‘uniform’, ‘quantile’, ‘kmeans’}
。
pandas 中已经有 cut()
函数,所以我想我需要创建一个自定义转换器来包装 cut()
函数行为。
期望的行为(不是实际的)
X = [[-2, -1, -0.5, 0, 0.5, 1, 2]]
est = Discretizer(bins=[-float("inf"), -1.0, 0.0, 1.0, float("inf")],
encode='ordinal')
est.fit(X)
est.transform(X)
# >>> array([[0., 0., 1., 1., 2., 2., 3.]])
上面的结果假定 bins 包括最右边的边缘和最低的边缘。像这样 pd.cut()
命令将提供:
import pandas as pd
import numpy as np
pd.cut(np.array([-2, -1, -0.5, 0, 0.5, 1, 2]),
[-float("inf"), -1.0, 0.0, 1.0, float("inf")],
labels=False, right=True, include_lowest=True)
# >>> array([0, 0, 1, 1, 2, 2, 3])
作为自定义转换器,这似乎对我有用。 scikit-learn 需要数字数组,所以我不确定您是否可以实现 pd.cut()
的功能,即 return 标签。出于这个原因,我在下面的实现中将其硬编码为 False
。
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
class CutTransformer(BaseEstimator, TransformerMixin):
def __init__(self, bins, right=True, retbins=False,
precision=3, include_lowest=False,
duplicates='raise'):
self.bins = bins
self.right = right
self.labels = False
self.retbins = retbins
self.precision = precision
self.include_lowest = include_lowest
self.duplicates = duplicates
def fit(self, X, y=None):
return self
def transform(self, X, y=None):
assert isinstance(X, pd.DataFrame)
for jj in range(X.shape[1]):
X.iloc[:, jj] = pd.cut(x=X.iloc[:, jj].values, **self.__dict__)
return X
一个例子
df = pd.DataFrame(data={'rand': np.random.rand(5)})
df
rand
0 0.030653
1 0.542533
2 0.159646
3 0.963112
4 0.539530
ct = CutTransformer(bins=np.linspace(0, 1, 5))
ct.transform(df)
rand
0 0
1 2
2 0
3 3
4 2
自定义转换器的替代方法会产生更多开销,它是使用 FunctionTransformer()
方法,该方法适用于无状态操作,例如预定义 bin 的情况。
import pandas as pd
from sklearn.preprocessing import FunctionTransformer
from sklearn.pipeline import make_pipeline
def ftransformer_cut(X, **kwargs):
if 'labels' not in kwargs:
kwargs['labels'] = False
assert isinstance(X, np.ndarray)
assert kwargs['labels'] == False
for jj in range(X.shape[1]):
X[:, jj] = pd.cut(x=X[:, jj], **kwargs)
return X
pipeline = make_pipeline(
FunctionTransformer(ftransformer_cut,
kw_args={'bins': np.linspace(0, 1, 5)})
)
df = pd.DataFrame(data={'rand': np.random.rand(5)})
rand
0 0.823234
1 0.336883
2 0.713595
3 0.408184
4 0.038
pipeline.transform(df)
array([[3.],
[1.],
[2.],
[1.],
[0.]])
唯一的问题是您只是转换传入数据,而不是在拟合阶段从训练中学习 bin 并在转换阶段使用该信息。理想情况下,您应该在拟合期间学习 bin 边缘并在变换期间分配 bin。