平均数编码:针对某个分类特征类别基数特别大的编码方式

原文:https://zhuanlan.zhihu.com/p/26308272

  插入一条信息:特征编码一定要考虑是否需要距离度量,编码方式对距离度量的适应:例如:我们用one-hot编码颜色,向量正交,各个颜色之间的距离等同,如果此处用序数编码显然不太合适,但是我们用one-hot编码星期几就不好了,显然星期一和星期二的距离小于星期一和星期三的距离。

  应用条件:某一个特征是分类的,特征的可能值非常多,那么平均数编码是一种高效的编码方式。

  适用问题:平均数编码是一种有监督的编码方式,适用于分类和回归问题。

  基本原理:基于分类问题分析

  目标y一共有C个不同类别,具体的一个类别用target表示;

  某一个定性特征variable一共有K个不同类别,具体的一个类别用k表示;

  先验概率:数据点属于某一个target(y)的概率,

  后验概率:该定性特征属于某一类时,数据点属于某一个target(y)的概率,;  

  算法的基本思想:将variable中的每一个k,都表示为它所对应的目标y值概率:

  因此,整个数据集将增加(C-1)列,是C-1而不是C的原因:,所以最后一个的概率值必然和其他的概率值线性相关。在线性模型、神经网络以及SVM里,不能加入线性相关的特征列。如果你使用的是基于决策树的模型(gbdt、rf等),个人仍然不推荐这种over-parameterization;

  先验概率与后验概率的计算:

   = (y = target)的数量 / y 的总数

   = (y = target 并且 variable = k)的数量 / (variable = k)的数量

  我们已经得到了先验概率估计和后验概率估计。最终编码所使用的概率估算,应当是先验概率与后验概率的一个凸组合(convex combination)。由此,我们引入先验概率的权重来计算编码所用概率

  

  直觉上,(贝叶斯统计学中也被称为shrinkage parameter)应该具有以下特性:

  1. 如果测试集中出现了新的特征类别(未在训练集中出现),那么
  2. 一个特征类别在训练集内出现的次数越多,后验概率的可信度越高,其权重也越大;  

  权重函数:

  我们需要定义一个权重函数,输入是特征类别在训练集中出现的次数n,输出是对于这个特征类别的先验概率的权重。假设一个特征类别的出现次数为n,以下是一个常见的权重函数:

  

  这个函数有两个参数:

  k:当时,,先验概率与后验概率的权重相同;当时,

  f:控制函数在拐点附近的斜率,f越大,“坡”越缓。

  图示:k=1时,不同的f对于函数图象的影响。

  

  当(n - k) / f太大的时候,np.exp可能会产生overflow的警告。我们不需要管这个警告,因为某一类别的频数极高,分母无限时,最终先验概率的权重将成为0,这也表示我们对于后验概率有充足的信任。  

  以上的算法设计能解决多个类别的分类问题,自然也能解决更简单的两类分类问题以及回归问题。

  还有一种情况:定性特征本身包括了不同级别。例如,国家包含了省,省包含了市,市包含了街区。有些街区可能就包含了大量的数据点,而有些省却可能只有稀少的几个数据点。这时,我们的解决方法是,在empirical bayes里加入不同层次的先验概率估计。  

  代码实现:

  原论文并没有提到,如果fit时使用了全部的数据,transform时也使用了全部数据,那么之后的机器学习模型会产生过拟合。因此,我们需要将数据分层分为n_splits个fold,每一个fold的数据都是利用剩下的(n_splits - 1)个fold得出的统计数据进行转换。n_splits越大,编码的精度越高,但也更消耗内存和运算时间。编码完毕后,是否删除原始特征列,应当具体问题具体分析。

  

import numpy as np
import pandas as pd
from sklearn.model_selection import StratifiedKFold
from itertools import product

class MeanEncoder:
    def __init__(self, categorical_features, n_splits=5, target_type=‘classification‘, prior_weight_func=None):
        """
        :param categorical_features: list of str, the name of the categorical columns to encode
        :param n_splits: the number of splits used in mean encoding
        :param target_type: str, ‘regression‘ or ‘classification‘
        :param prior_weight_func:
        a function that takes in the number of observations, and outputs prior weight when a dict is passed, the default exponential decay function will be used:
        k: the number of observations needed for the posterior to be weighted equally as the prior
        f: larger f --> smaller slope
        """
        self.categorical_features = categorical_features
        self.n_splits = n_splits
        self.learned_stats = {}
        if target_type == ‘classification‘:
            self.target_type = target_type
            self.target_values = []
        else:
            self.target_type = ‘regression‘
            self.target_values = None
        if isinstance(prior_weight_func, dict):
            self.prior_weight_func = eval(‘lambda x: 1 / (1 + np.exp((x - k) / f))‘, dict(prior_weight_func, np=np))
        elif callable(prior_weight_func):
            self.prior_weight_func = prior_weight_func
        else:
            self.prior_weight_func = lambda x: 1 / (1 + np.exp((x - 2) / 1))

    @staticmethod
    def mean_encode_subroutine(X_train, y_train, X_test, variable, target, prior_weight_func):
        X_train = X_train[[variable]].copy()
        X_test = X_test[[variable]].copy()
        if target is not None:
            nf_name = ‘{}_pred_{}‘.format(variable, target)
            X_train[‘pred_temp‘] = (y_train == target).astype(int) #classification
        else:
            nf_name = ‘{}_pred‘.format(variable)
            X_train[‘pred_temp‘] = y_train  #regression
        prior = X_train[‘pred_temp‘].mean()
        col_avg_y = X_train.groupby(by=variable, axis=0)[‘pred_temp‘].agg({‘mean‘: ‘mean‘, ‘beta‘: ‘size‘})
        col_avg_y[‘beta‘] = prior_weight_func(col_avg_y[‘beta‘])
        col_avg_y[nf_name] = col_avg_y[‘beta‘] * prior + (1 - col_avg_y[‘beta‘]) * col_avg_y[‘mean‘]
        col_avg_y.drop([‘beta‘, ‘mean‘], axis=1, inplace=True)
        nf_train = X_train.join(col_avg_y, on=variable)[nf_name].values
        nf_test = X_test.join(col_avg_y, on=variable).fillna(prior, inplace=False)[nf_name].values
        return nf_train, nf_test, prior, col_avg_y

    def fit_transform(self, X, y):
        """
        :param X: pandas DataFrame, n_samples * n_features
        :param y: pandas Series or numpy array, n_samples
        :return X_new: the transformed pandas DataFrame containing mean-encoded categorical features
        """
        X_new = X.copy()
        if self.target_type == ‘classification‘:
            skf = StratifiedKFold(self.n_splits)
        else:
            skf = KFold(self.n_splits)
        if self.target_type == ‘classification‘:
            self.target_values = sorted(set(y))
            self.learned_stats = {‘{}_pred_{}‘.format(variable, target): [] for variable, target in product(self.categorical_features, self.target_values)}
            for variable, target in product(self.categorical_features, self.target_values):
                nf_name = ‘{}_pred_{}‘.format(variable, target)
                X_new.loc[:, nf_name] = np.nan
                for large_ind, small_ind in skf.split(y, y):
                    nf_large, nf_small, prior, col_avg_y = MeanEncoder.mean_encode_subroutine(X_new.iloc[large_ind],y.iloc[large_ind],X_new.iloc[small_ind],variable, target, self.prior_weight_func)
                    X_new.iloc[small_ind, -1] = nf_small
                    self.learned_stats[nf_name].append((prior, col_avg_y))
        else:
            self.learned_stats = {‘{}_pred‘.format(variable): [] for variable in self.categorical_features}
            for variable in self.categorical_features:
                nf_name = ‘{}_pred‘.format(variable)
                X_new.loc[:, nf_name] = np.nan
                for large_ind, small_ind in skf.split(y, y):
                    nf_large, nf_small, prior, col_avg_y = MeanEncoder.mean_encode_subroutine(
                        X_new.iloc[large_ind], y.iloc[large_ind], X_new.iloc[small_ind], variable, None, self.prior_weight_func)
                    X_new.iloc[small_ind, -1] = nf_small
                    self.learned_stats[nf_name].append((prior, col_avg_y))
        return X_new

    def transform(self, X):
        """
        :param X: pandas DataFrame, n_samples * n_features
        :return X_new: the transformed pandas DataFrame containing mean-encoded categorical features
        """
        X_new = X.copy()
        if self.target_type == ‘classification‘:
            for variable, target in product(self.categorical_features, self.target_values):
                nf_name = ‘{}_pred_{}‘.format(variable, target)
                X_new[nf_name] = 0
                for prior, col_avg_y in self.learned_stats[nf_name]:
                    X_new[nf_name] += X_new[[variable]].join(col_avg_y, on=variable).fillna(prior, inplace=False)[nf_name]
                X_new[nf_name] /= self.n_splits
        else:
            for variable in self.categorical_features:
                nf_name = ‘{}_pred‘.format(variable)
                X_new[nf_name] = 0
                for prior, col_avg_y in self.learned_stats[nf_name]:
                    X_new[nf_name] += X_new[[variable]].join(col_avg_y, on=variable).fillna(prior, inplace=False)[nf_name]
                X_new[nf_name] /= self.n_splits
        return X_new

  

  

  

  

  

  

原文地址:https://www.cnblogs.com/wzdLY/p/9639519.html

时间: 2024-11-16 00:44:35

平均数编码:针对某个分类特征类别基数特别大的编码方式的相关文章

IOS开发基础篇 -- 分类、类别

Category (分类.类别) 分类只能增加方法不能增加成员变量,可以声明属性,实际上会生成属性的set和get方法,但不会生成带下划线的成员变量,也不能添加成员变量(可以在不修改原来类模型的基础上拓充方法) inheritance >继承可以扩充成员变量和方法 但会产生新的类 extensions >类扩展(没有名字) 可以增加私有的成员变量和方法  一般写在要扩展类的.m文件内

分类(类别/Category)与 类扩展(Extension)

一.分类(类别/Category) 1.适用范围 当你已经封装好了一个类(也可能是系统类.第三方库),不想在改动这个类了,可是随着程序功能的增加需要在类中增加一个方法,这时我们不必修改主类,只需要给你原来的类增加一个分类. 将一个大型的类拆分成不同的分类,在不同分类中实现类别声明的方法,这样可以将一个类的实现写到多个.m文件中,方便管理和协同开发. 分类中的方法可以只声明,不实现,所以在协议不支持可选方法的时候(协议现在已经支持可选方法),通常把分类作为非正式协议使用. 2.语法格式 文件中的语

数据预处理 | 使用 sklearn.preprocessing.OrdinalEncoder 将分类特征转换为数值型

from sklearn.preprocessing import OrdinalEncoder model_oe = OrdinalEncoder() string_data_array = model_oe.fit_transform(string_data) string_data_pd = pd.DataFrame(string_data_con,columns=string_data.columns) 说明: 1  string_data  是挑出来的 需要转成数值型特征的 分类特征

关于URL编码(针对URL含有中文的参数)

http://www.ruanyifeng.com/blog/2010/02/url_encoding.html 一.问题的由来 URL就是网址,只要上网,就一定会用到. 一般来说,URL只能使用英文字母.阿拉伯数字和某些标点符号,不能使用其他文字和符号.比如,世界上有英文字母的网址"http://www.abc.com",但是没有希腊字母的网址"http://www.aβγ.com"(读作阿尔法-贝塔-伽玛.com).这是因为网络标准RFC 1738做了硬性规定:

SQL安装过程中“针对SQL Server 注册表的一致性验证“出错解决方式

1.打开注册表,查找到[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\0804],分别打开Counter 和Help 2.打开Counter,把滚动条拉倒最后,然后记下最大的那个值(不同计算机不一样的),记下后关闭窗口 3.同样的打开Help,滚动到最后记最大的那个值(你们的值不一定跟我一样的哦!!!),记下后关闭窗口 4,再重新定位到Perflib的节点上, 5.双击Last Counter然后在

PHP 页面编码声明与用header或meta实现PHP页面编码的区别

php的header来定义一个php页面为utf编码或GBK编码 php页面为utf编码 header("Content-type: text/html; charset=utf-8"); php页面为gbk编码 header("Content-type: text/html; charset=gb2312"); php页面为big5编码 header("Content-type: text/html; charset=big5"); 通常情况以

飞行器的分类四轴飞行器实践教程大学霸

飞行器的分类四轴飞行器实践教程大学霸 1.2  飞行器的分类 看过了飞行器的历史,你会认为飞行器的历史发展很慢.不过,在这里我想说,最近的一百多年是一个知识爆炸的年代.虽然飞行器才发展没有多长时间,但是他的种类繁多让人眼花缭乱!以下就按照飞行器的运行环境和原理分类介绍了几种飞行器本文选自四轴飞行器实践教程大学霸. 1.2.1  航空器 航空器是指在大气层内飞行的飞行器,主要依靠空气的静浮力或与空气进行相对运动时产生的空气动力升空飞行.典型的例子有气球.飞艇.飞机滑翔翼.直升机.旋翼机(四轴飞行器

【deep learning精华部分】稀疏自编码提取高阶特征、多层微调完全解释及代码逐行详解

我们前面已经讲了如何训练稀疏自编码神经网络,当我们训练好这个神经网络后,当有新的样本输入到这个训练好的稀疏自编码器中后,那么隐藏层各单元的激活值组成的向量就可以代表(因为根据稀疏自编码,我们可以用来恢复),也就是说就是在新的特征下的特征值.每一个特征是使某一个取最大值的输入.假设隐藏层单元有200个,那么就一共有200个特征,所以新的特征向量有200维.特征显示情况在前面博客中已经给出,我们把这时候的特征称为一阶特征. 我们知道脑神经在处理问题,比如看一个图片的时候,也不只使用了一层的神经,而是

针对二元分类机器学习模型中的准确率(Precision)、召回率(Recall)、F值(F-Measure)的学习

一.首先了解混淆矩阵中的四个值对应的意思: True Positive(真正,TP):将正类预测为正类数  True Negative(真负,TN):将负类预测为负类数  False Positive(假正,FP):将负类预测为正类数误报 (Type I error)  False Negative(假负,FN):将正类预测为负类数漏报 (Type II error) 如下图所示: 二.评价指标的意义 1.准确率(Accuracy) 注:准确率是我们最常见的评价指标,而且很容易理解,就是被分对的