问题描述
KBinsDiscretizer
分箱数字数据。
我有 string 数据整个 value_counts
看起来像这样:
MTS RUS 495
Tele2 484
MegaFon 437
Beeline 431
Vodafone UA 402
...
3 Austria 1
FJ VODAFONE | Vodafone 1
Babilon-M 1
MOOV BENIN 1
3 | Beeline 1
Name: carrier,Length: 822,dtype: int64
这已经是分类的了,但是有太多不同的值(822!)。
我想用相同的高度分箱(如 strategy="quantile"
中的 KBinsdiscretizer
)将其分箱为很少的不同值 (5-10)。
算法很简单:将KBinsdiscretizer
应用于转换为value_counts
中值的数值等级的数据。
我想知道是否已经有一种方法可以做到(如果没有,如何按照惯用方式做到这一点)。
附注。一个关键的限制是该方法必须是“通用的”,我不能检查每个字符串列,我必须单独优化分箱。
解决方法
这是我拼凑的东西,它似乎有效,但我希望我可以使用 OOTB 的东西:
import sklearn
from sklearn.base import TransformerMixin,BaseEstimator
def only_column(df):
if df.shape[1] != 1:
raise ValueError("only_column",df.shape)
return df[df.columns[0]]
class ObjectDiscretizer(TransformerMixin,BaseEstimator):
def __init__(self,**kwargs):
self.kbin_discr = sklearn.preprocessing.KBinsDiscretizer(**kwargs)
self.name2rank = None
self.names = None
self.ranks = None
def __str__(self):
if self.name2rank is None:
return "%s(%s)" % (self.__class__.__name__,self.kbin_discr)
return "%s(n=%d,b=%d,%s)" % (
self.__class__.__name__,len(self.name2rank),len(self.kbin_discr.bin_edges_),self.kbin_discr)
def fit(self,X,y=None):
"Learn how to discretize the data."
col = only_column(X).astype(str)
# np.unique does not always work
# https://github.com/numpy/numpy/issues/18288
vc = col.value_counts(dropna=False)
self.name2rank = {v:i for i,v in enumerate(vc.index)}
self.names,self.ranks = np.array(list(zip(*sorted(self.name2rank.items()))))
self.kbin_discr.fit(col.replace(self.name2rank).values.reshape((-1,1)))
return self
def transform(self,X):
"Discretize the data."
return self.kbin_discr.transform(self.ranks[np.searchsorted(self.names,X.astype(str))]).astype(int)