合并回归树的分类变量级别

机器算法验证 回归 分类数据 大车 层次聚类 多类
2022-03-31 08:09:16

我有一个数据集,我想对其进行回归分析。分类类型和连续类型都有许多特征。其中一个分类特征有很多(> 75)级别,所以这是一个问题。我有理由相信,有些级别本质上是相同的。我打算将决策树与一些集成方法(即 Bagging 或 boosting)一起使用。

我想尝试汇集/聚集有问题的功能的级别以提高性能。我意识到理论上如果集合/叶子的数量足够大,这是没有必要的,但我已经遇到了计算问题。

是否有标准方法来组合执行相同的级别?

- - - - - - -编辑 - - - - - - -

我想我找到了一种可行的方法。这个想法是使用邻近矩阵这本质上是 N_Obs by N_Obs 矩阵,用于袋外树的一部分,其中观察值位于同一终端节点中。然后我们可以将其聚合成一个逐级矩阵,其中元素是邻近矩阵中分数的平均值。然后,当所有级别超过阈值时,我们会将所有级别汇总在一起,看看这是否会提高 RMSE。最好采用逐步迭代的方法来找到最佳池,但我可能只是将阈值作为对角线的平均值。这应该给出一个合理的阈值,因为它表示每个级别与其自身位于同一终端节点中的频率。欢迎评论,我会报告结果。

1个回答

我已经实施了我的解决方案。我写了两个函数:

prox_matrix(df,目标,特征,cluster_dimension,trees = 10)

参数

  • df : 输入数据框
  • 目标:您尝试使用随机福雷斯特预测的因变量
  • 特征:自变量列表
  • cluster_dimension:您想要集群/池以添加到您的功能列表中的维度
  • :在你的随机森林中使用的树的数量

退货

  • D : cluster_dimension 的邻近矩阵的 DataFrame

下面的代码

def prox_matrix(df, target, features, cluster_dimension,trees = 10):
    #https://www.stat.berkeley.edu/~breiman/RandomForests/cc_home.htm#prox

    from sklearn.ensemble import RandomForestRegressor
    import numpy as np
    import pandas as pd

    #initialize datframe for independant variables
    independant = pd.DataFrame()

    #Handle Categoricals: This should really be added to RandomForestRegressor
    for column,data_type in df[features].dtypes.iteritems():       
        try:
            independant[column] = pd.to_numeric(df[column],downcast = 'integer')
        except ValueError:
            contains_nulls = df[column].isnull().values.any()
            dummies = pd.get_dummies(df[column],prefix=column,dummy_na=contains_nulls,drop_first=True)
            independant[dummies.columns] = dummies

    if len(independant.index) != len(df.index):
        raise Exception('independant variables not stored properly')

    #train Model    
    clf = RandomForestRegressor(n_estimators=trees, n_jobs=-1)
    clf.fit(independant, df[target])

    #Final leaf for each tree
    leaves = clf.apply(independant)
    #value in cluster dimension
    labels = df[cluster_dimension].values

    numerator_matrix = {}
    for i,value_i in enumerate(labels):
        for j,value_j in enumerate(labels):
            if i >= j:       
                numerator_matrix[(value_i,value_j)] = numerator_matrix.get((value_i,value_j), 0) + np.count_nonzero(leaves[i]==leaves[j])
                numerator_matrix[(value_j,value_i)] = numerator_matrix[(value_i,value_j)] 

    #normalize by the total number of possible matchnig leaves        
    prox_matrix = {key: 1.0 - float(x)/(trees*np.count_nonzero(labels==key[0])*np.count_nonzero(labels==key[1])) for key, x in numerator_matrix.iteritems()}                                                                  

    #make sorted dataframe                                                                                                                                                                                                                                                                
    levels = np.unique(labels)
    D = pd.DataFrame(data=[[ prox_matrix[(i,j)] for i in levels] for j in levels],index=levels,columns=levels)

    return D

kMedoids(D, k, tmax=100)

参数

  • D:接近/距离矩阵
  • k : 簇数
  • tmax:检查聚类收敛的最大迭代次数

退货

  • M : 媒体列表
  • C:字典将聚类级别映射到每个媒体
  • S:用于评估性能的每个集群的轮廓

下面的代码

def kMedoids(D, k, tmax=100):
    #https://www.researchgate.net/publication/272351873_NumPy_SciPy_Recipes_for_Data_Science_k-Medoids_Clustering
    import numpy as np
    import pandas as pd

    # determine dimensions of distance matrix D
    m, n = D.shape

    if m != n:
        raise Exception('matrix not symmetric')

    if sum(D.columns.values != D.index.values):
        raise Exception('rows and columns do not match')

    if k > n:
        raise Exception('too many medoids')

    #Some distance matricies will not have a 0 diagonal    
    Dtemp =D.copy()
    np.fill_diagonal(Dtemp.values,0)

    # randomly initialize an array of k medoid indices
    M = list(Dtemp.sample(k).index.values)

    # initialize a dictionary to represent clusters
    Cnew = {}

    for t in xrange(tmax):    
        # determine mapping to clusters
        J = Dtemp.loc[M].idxmin(axis='index')
        #Fill dictionary with cluster members
        C = {kappa: J[J==kappa].index.values for kappa in J.unique()}  
        # update cluster medoids
        Cnew = {Dtemp.loc[C[kappa],C[kappa]].mean().idxmin() : C[kappa] for kappa in C.keys()}       
        #Update mediod list
        M = Cnew.keys()

        # check for convergence (ie same clusters)
        if set(C.keys()) == set(Cnew.keys()):
            if not sum(set(C[kappa]) != set(Cnew[kappa]) for kappa in C.keys()): break            
    else:        
        print('did not converge')

    #Calculate silhouette 
    S = {}
    for kappa_same in Cnew.keys():
        a = Dtemp.loc[Cnew[kappa_same],Cnew[kappa_same]].mean().mean()
        b = np.min([Dtemp.loc[Cnew[kappa_other],Cnew[kappa_same]].mean().mean() for kappa_other in Cnew.keys() if kappa_other!=kappa_same])
        S[kappa_same] = (b - a) / max(a, b)

    # return results
    return M, Cnew, S

笔记:

  1. 代码中有指向理论文档的链接
  2. 我使用了所有记录,而不是严格意义上的 OOB 记录。在这里跟进
  3. prox_matrix() 方法非常慢。我做了一些事情来加速它,但大部分成本来自双循环。欢迎更新。
  4. 邻近矩阵的对角线不必为零。我在 KMedoids 方法中强制执行此操作,以便获得收敛。