Python实现连续变量最优分箱详解:基于CART算法

Dylan
Dylan
发布时间:2026-10-13| 5人浏览过
原创

本文详细介绍如何利用Python及CART算法对连续变量进行最优分箱。通过计算基尼系数下降程度确定最佳切分点,并设定叶子节点样本量阈值作为终止条件。文中提供完整代码实现,包括中位数计算、递归分割及结果验证,适用于信用评分等场景。

变量分箱方法概述

变量分箱技术主要划分为两大类别:有监督型与无监督型。

常见的分箱方法包括以下几种:

A. 无监督方法:(1) 等宽分箱 (2) 等频分箱 (3) 聚类分箱

B. 有监督方法:(1) 卡方分箱法(ChiMerge) (2) 基于ID3、C4.5、CART等单变量决策树算法 (3) 信用评分建模中的IV最大化分箱等

CART算法原理与终止条件

本篇内容将使用Python语言,基于CART算法实现连续变量的最优分箱。

由于CART属于决策树分类算法,因此该过程可视为单变量决策树分类的应用。

简要介绍其理论基础:

CART构建的是二叉树,每次仅执行二元分类。针对连续性变量,具体做法是依次计算相邻两个元素值的中位数,从而将数据集划分为两部分。随后计算以该点作为切割点时,基尼系数相较于分割前的下降幅度。每次切分时,选取使基尼系数下降程度最大的点作为最优切分点,接着对切分后的子数据集按照相同原则继续切分,直至满足终止条件为止。

关于CART分类的终止条件:需视具体应用场景而定。在本案例中,设置如下两个条件:a. 每个叶子节点的样本量需大于等于总样本量的5%;b. 内部节点再进行划分所需的最小样本数需大于等于总样本量的10%。

Python代码实现与验证

以下是Python代码的具体实现:


import pandas as pd
import numpy as np
 
#读取数据集,至少包含变量和target两列
sample_set = pd.read_excel('/数据样本.xlsx')
 
def calc_score_median(sample_set, var):
  '''
  计算相邻评分的中位数,以便进行决策树二元切分
  param sample_set: 待切分样本
  param var: 分割变量名称
  '''
  var_list = list(np.unique(sample_set[var]))
  var_median_list = []
  for i in range(len(var_list) -1):
    var_median = (var_list[i] + var_list[i+1]) / 2
    var_median_list.append(var_median)
  return var_median_list

变量var表示需要进行分箱的目标变量名,该函数返回一个包含样本变量中位数的列表。


def choose_best_split(sample_set, var, min_sample):
  '''
  使用CART分类决策树选择最好的样本切分点
  返回切分点
  param sample_set: 待切分样本
  param var: 分割变量名称
  param min_sample: 待切分样本的最小样本量(限制条件)
  '''
  # 根据样本评分计算相邻不同分数的中间值
  score_median_list = calc_score_median(sample_set, var)
  median_len = len(score_median_list)
  sample_cnt = sample_set.shape[0]
  sample1_cnt = sum(sample_set['target'])
  sample0_cnt = sample_cnt- sample1_cnt
  Gini = 1 - np.square(sample1_cnt / sample_cnt) - np.square(sample0_cnt / sample_cnt)
  
  bestGini = 0.0; bestSplit_point = 0.0; bestSplit_position = 0.0
  for i in range(median_len):
    left = sample_set[sample_set[var] < score_median_list[i]]
    right = sample_set[sample_set[var] > score_median_list[i]]
    
    left_cnt = left.shape[0]; right_cnt = right.shape[0]
    left1_cnt = sum(left['target']); right1_cnt = sum(right['target'])
    left0_cnt = left_cnt - left1_cnt; right0_cnt = right_cnt - right1_cnt
    left_ratio = left_cnt / sample_cnt; right_ratio = right_cnt / sample_cnt
    
    if left_cnt < min_sample or right_cnt < min_sample:
      continue
    
    Gini_left = 1 - np.square(left1_cnt / left_cnt) - np.square(left0_cnt / left_cnt)
    Gini_right = 1 - np.square(right1_cnt / right_cnt) - np.square(right0_cnt / right_cnt)
    Gini_temp = Gini - (left_ratio * Gini_left + right_ratio * Gini_right)
    if Gini_temp > bestGini:
      bestGini = Gini_temp; bestSplit_point = score_median_list[i]
      if median_len > 1:
        bestSplit_position = i / (median_len - 1)
      else:
        bestSplit_position = i / median_len
    else:
      continue
        
  Gini = Gini - bestGini
  return bestSplit_point, bestSplit_position

参数min_sample表示最小叶子节点的样本阈值,若子集样本量小于该阈值则不进行切分,如前文所述,此处设置为整体样本量的5%。

返回的结果在此处仅包含最优分割点,若需获取其他指标如GINI值,可自行扩展代码添加。


def bining_data_split(sample_set, var, min_sample, split_list):
  '''
  划分数据找到最优分割点list
  param sample_set: 待切分样本
  param var: 分割变量名称
  param min_sample: 待切分样本的最小样本量(限制条件)
  param split_list: 最优分割点list
  '''
  split, position = choose_best_split(sample_set, var, min_sample)
  if split != 0.0:
    split_list.append(split)
  # 根据分割点划分数据集,继续进行划分
  sample_set_left = sample_set[sample_set[var] < split]
  sample_set_right = sample_set[sample_set[var] > split]
  # 如果左子树样本量超过2倍最小样本量,且分割点不是第一个分割点,则切分左子树
  if len(sample_set_left) >= min_sample * 2 and position not in [0.0, 1.0]:
    bining_data_split(sample_set_left, var, min_sample, split_list)
  else:
    None
  # 如果右子树样本量超过2倍最小样本量,且分割点不是最后一个分割点,则切分右子树
  if len(sample_set_right) >= min_sample * 2 and position not in [0.0, 1.0]:
    bining_data_split(sample_set_right, var, min_sample, split_list)
  else:
    None

参数split_list用于保存返回的切分点,每次切分后生成的切分点将存入该列表中。

在此处判断切分点分割出的左子树和右子树是否满足“内部节点再划分所需的最小样本数大于等于总样本量的10%”这一条件,若满足则进行递归调用。


def get_bestsplit_list(sample_set, var):
  '''
  根据分箱得到最优分割点list
  param sample_set: 待切分样本
  param var: 分割变量名称
  '''
  # 计算最小样本阈值(终止条件)
  min_df = sample_set.shape[0] * 0.05
  split_list = []
  # 计算第一个和最后一个分割点
  bining_data_split(sample_set, var, min_df, split_list)
  return split_list

最后整合上述几个函数调用,返回一个分割点列表。

可以使用sklearn库中的决策树进行单变量分类测试,以验证结果。在分类方法相同且剪枝条件一致的情况下,两者结果应保持一致。

以上关于Python实现连续变量最优分箱的CART算法详解即为分享的全部内容,希望能为大家提供参考,也希望大家多多支持本站。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。