目录

1 为什么要记录特征转换行为?
2 有哪些特征转换的方式?
3 特征转换的组合
4 sklearn源码分析
  4.1 一对一映射
  4.2 一对多映射
  4.3 多对多映射
5 实践
6 总结
7 参考资料


1 为什么要记录特征转换行为?

  使用机器学习算法和模型进行数据挖掘,有时难免事与愿违:我们依仗对业务的理解,对数据的分析,以及工作经验提出了一些特征,但是在模型训练完成后,某些特征可能“身微言轻”——我们认为相关性高的特征并不重要,这时我们便要反思这样的特征提出是否合理;某些特征甚至“南辕北辙”——我们认为正相关的特征结果变成了负相关,造成这种情况很有可能是抽样与整体不相符,模型过于复杂,导致了过拟合。然而,我们怎么判断先前的假设和最后的结果之间的差异呢?

  线性模型通常有含有属性coef_,当系数值大于0时为正相关,当系数值小于0时为负相关;另外一些模型含有属性feature_importances_,顾名思义,表示特征的重要性。根据以上两个属性,便可以与先前假设中的特征的相关性(或重要性)进行对比了。但是,理想是丰满的,现实是骨感的。经过复杂的特征转换之后,特征矩阵X已不再是原来的样子:哑变量使特征变多了,特征选择使特征变少了,降维使特征映射到另一个维度中。

  累觉不爱了吗?如果,我们能够将最后的特征与原特征对应起来,那么分析特征的系数和重要性又有了意义了。所以,在训练过程(或者转换过程)中,记录下所有特征转换行为是一个有意义的工作。可惜,sklearn暂时并没有提供这样的功能。在这篇博文中,我们尝试对一些常见的转换功能进行行为记录,读者可以在此基础进行进一步的拓展。


2 有哪些特征转换的方式?

  《使用sklearn做单机特征工程》一文概括了若干常见的转换功能:

类名 功能 说明
StandardScaler 数据预处理(无量纲化) 标准化,基于特征矩阵的列,将特征值转换至服从标准正态分布
MinMaxScaler 数据预处理(无量纲化) 区间缩放,基于最大最小值,将特征值转换到[0, 1]区间上
Normalizer 数据预处理(归一化) 基于特征矩阵的行,将样本向量转换为“单位向量”
Binarizer 数据预处理(二值化) 基于给定阈值,将定量特征按阈值划分
OneHotEncoder 数据预处理(哑编码) 将定性数据编码为定量数据
Imputer 数据预处理(缺失值计算) 计算缺失值,缺失值可填充为均值等
PolynomialFeatures 数据预处理(多项式数据转换) 多项式数据转换
FunctionTransformer 数据预处理(自定义单元数据转换) 使用单变元的函数来转换数据
VarianceThreshold 特征选择(Filter) 方差选择法
SelectKBest 特征选择(Filter) 可选关联系数、卡方校验、最大信息系数作为得分计算的方法
RFE 特征选择(Wrapper) 递归地训练基模型,将权值系数较小的特征从特征集合中消除
SelectFromModel 特征选择(Embedded) 训练基模型,选择权值系数较高的特征
PCA 降维(无监督) 主成分分析法
LDA 降维(有监督) 线性判别分析法

  按照特征数量是否发生变化,这些转换类可分为:

  • 无变化:StandardScaler,MinMaxScaler,Normalizer,Binarizer,Imputer,FunctionTransformer*
  • 有变化:OneHotEncoder,PolynomialFeatures,VarianceThreshold,SelectKBest,RFE,SelectFromModel,PCA,LDA
FunctionTransformer*:自定义的转换函数通常不会使特征数量发生变化

  对于不造成特征数量变化的转换类,我们只需要保持特征不变即可。在此,我们主要研究那些有变化的转换类,其他转换类都默认为无变化。按照映射的形式,可将以上有变化的转换类可分为:

  • 一对一:VarianceThreshold,SelectKBest,RFE,SelectFromModel
  • 一对多:OneHotEncoder
  • 多对多:PolynomialFeatures,PCA,LDA

  原特征与新特征为一对一映射通常发生在特征选择时,若原特征被选择则直接变成新特征,否则抛弃。哑编码为典型的一对多映射,需要哑编码的原特征将会转换为多个新特征。多对多的映射中PolynomialFeatures并不要求每一个新特征都与原特征建立映射关系,例如阶为2的多项式转换,第一个新特征只由第一个原特征生成(平方)。降维的本质在于将原特征矩阵X映射到维度更低的空间中,使用的技术通常是矩阵乘法,所以它既要求每一个原特征映射到所有新特征,同时也要求每一个新特征被所有原特征映射。


3 特征转换的组合

  在《使用sklearn优雅地进行数据挖掘》一文中,我们看到一个基本的数据挖掘场景:

  特征转换行为通常是流水线型和并行型结合的。所以,我们考虑重新设计流水线处理类Pipeline和并行处理类FeatureUnion,使其能够根据不同的特征转换类,记录下转换行为“日志”。“日志”的表示形式也是重要的,由上图可知,集成后的特征转换过程呈现无环网状,故使用网络来描述“日志”是合适的。在网络中,节点表示特征,有向连线表示特征转换。

  为此,我们新增两个类型Feature和Transfrom来构造网络结构,Feature类型表示网络中的节点,Transform表示网络中的有向边。python的networkx库可以很好地表述网络和操作网络,我这是要重新造轮子吗?其实并不是,现在考虑代表新特征的节点怎么命名的问题,显然,不能与网络中任意节点同名,否则会发生混淆。然而,由于sklearn的训练过程存在并行过程(线程),直接使用network来构造网络的话,将难以处理节点重复命名的问题。所以,我才新增两个新的类型来描述网络结构,这时网络中的节点名是可以重复的。最后,对这网络进行广度遍历,生成基于networkx库的网络,因为这个过程是串行的,故可以使用“当前节点数”作为新增节点的序号了。这两个类的代码(feature.py)设计如下:

  1. import numpy as np
  2.  
  3. class Transform(object):
  4. def __init__(self, label, feature):
  5. super(Transform, self).__init__()
  6. #边标签名,使用networkx等库画图时将用到
  7. self.label = label
  8. #该边指向的节点
  9. self.feature = feature
  10.  
  11. class Feature(object):
  12. def __init__(self, name):
  13. super(Feature, self).__init__()
  14. #节点名称,该名称在网络中不唯一,在某些映射中,该名称需要直接传给新特征
  15. self.name = name
  16. #节点标签名,该名称在网络中唯一,使用networkx等库画图时将用到
  17. self.label = '%s[%d]' % (self.name, id(self))
  18. #从本节点发出的有向边列表
  19. self.transformList = np.array([])
  20.  
  21. #建立从self到feature的有向边
  22. def transform(self, label, feature):
  23. self.transformList = np.append(self.transformList, Transform(label, feature))
  24.  
  25. #深度遍历输出以本节点为源节点的网络
  26. def printTree(self):
  27. print self.label
  28. for transform in self.transformList:
  29. feature = transform.feature
  30. print '--%s-->' % transform.label,
  31. feature.printTree()
  32.  
  33. def __str__(self):
  34. return self.label

4 sklearn源码分析

  我们可以统一地记录不改变特征数量的转换行为:在“日志”网络中,从代表原特征的节点,引伸出连线连上唯一的代表新特征的节点。然而,对于改变特征数量的转换行为来说,需要针对每个转换类编写不同的“日志”记录(网络生成)代码。为不改变特征数量的转换行为设计代码(default.py)如下:

  1. import numpy as np
  2. from feature import Feature
  3.  
  4. def doWithDefault(model, featureList):
  5. leaves = np.array([])
  6.  
  7. n_features = len(featureList)
  8.  
  9. #为每一个输入的原节点,新建一个新节点,并建立映射
  10. for i in range(n_features):
  11. feature = featureList[i]
  12. newFeature = Feature(feature.name)
  13. feature.transform(model.__class__.__name__, newFeature)
  14. leaves = np.append(leaves, newFeature)
  15.  
  16. #返回新节点列表,之所以该变量取名叫leaves,是因为其是网络的边缘节点
  17. return leaves

4.1 一对一映射

  映射形式为一对一时,转换类通常为特征选择类。在这种映射下,原特征要么只转化为一个新特征,要么不转化。通过分析sklearn源码不难发现,特征选择类都混入了特质sklearn.feature_selection.base.SelectorMixin,因此这些类都有方法get_support来获取哪些特征转换信息:

  所以,在设计“日志”记录模块时,判断转换类是否混入了该特征,若是则直接调用get_support方法来得到被筛选的特征的掩码或者下标,如此我们便可从被筛选的特征引伸出连线连上新特征。为此,我们设计代码(one2one.py)如下:

  1. import numpy as np
  2. from sklearn.feature_selection.base import SelectorMixin
  3. from feature import Feature
  4.  
  5. def doWithSelector(model, featureList):
  6. assert(isinstance(model, SelectorMixin))
  7.  
  8. leaves = np.array([])
  9.  
  10. n_features = len(featureList)
  11.  
  12. #新节点的掩码
  13. mask_features = model.get_support()
  14.  
  15. for i in range(n_features):
  16. feature = featureList[i]
  17. #原节点被选择,生成新节点,并建立映射
  18. if mask_features[i]:
  19. newFeature = Feature(feature.name)
  20. feature.transform(model.__class__.__name__, newFeature)
  21. leaves = np.append(leaves, newFeature)
  22. #原节点被抛弃,生成一个名为Abandomed的新节点,建立映射,但是这个特征不加入下一步继续生长的节点列表
  23. else:
  24. newFeature = Feature('Abandomed')
  25. feature.transform(model.__class__.__name__, newFeature)
  26.  
  27. return leaves

4.2 一对多映射

  OneHotEncoder是典型的一对多映射转换类,其提供了两个属性结合两个参数来表示转换信息:

  • n_values:定性特征的值数量,若为auto则直接从训练集中获取,若为整数则表示所有定性特征的值数量+1,若为数组则分别表示每个定性特征的数量+1
  • categorical_features:定性特征的掩码或下标
  • active_features_:有效值(在n_values为auto时有用),假设A属性取值范围为(1,2,3),但是实际上训练样本中只有(1,2),假设B属性取值范围为(2,3,4),训练样本中只有(2,4),那么有效值为(1,2,5,7)。是不是感到奇怪了,为什么有效值不是(1,2,2,4)?OneHotEncoder在这里做了巧妙的设计:有效值被转换成了一个递增的序列,这样方便于配合属性n_features快速地算出每个原特征转换成了哪些新特征,转换依据的真实有效值是什么。
  • feature_indices_:每个定性特征的有效值范围,例如第i个定性特征,其有效值范围为feature_indices_[i]至feature_indices_[i+1],sklearn官方文档在此描述有误,该数组的长度应为n_features+1。在上例中,feature_indices_等于(0,3,8)。故下标为0的定性特征,其有效值范围为大于0小于3,则有效值为1和2;下标为1的定性特征,其有效值范围为大于3小于8,则有效值为5和7。下标为0的定性特征,其两个真实有效值为1-0=1和2-0=2;下标为1的定性特征,其两个真实有效值为5-3=2和7-3=4。这样一来就可以得到(1,2,2,4)的真实有效值了。

  综上,我们设计处理OneHotEncoder类的代码(one2many.py)如下:

  1. import numpy as np
  2. from sklearn.preprocessing import OneHotEncoder
  3. from feature import Feature
  4.  
  5. def doWithOneHotEncoder(model, featureList):
  6. assert(isinstance(model, OneHotEncoder))
  7. assert(hasattr(model, 'feature_indices_'))
  8.  
  9. leaves = np.array([])
  10.  
  11. n_features = len(featureList)
  12.  
  13. #定性特征的掩码
  14. if model.categorical_features == 'all':
  15. mask_features = np.ones(n_features)
  16. else:
  17. mask_features = np.zeros(n_features)
  18. mask_features[self.categorical_features] = 1
  19.  
  20. #定性特征的数量
  21. n_qualitativeFeatures = len(model.feature_indices_) - 1
  22. #如果定性特征的取值个数是自动的,即从训练数据中生成
  23. if model.n_values == 'auto':
  24. #定性特征的有效取值列表
  25. n_activeFeatures = len(model.active_features_)
  26. #变量j为定性特征的下标,变量k为有效值的下标
  27. j = k = 0
  28. for i in range(n_features):
  29. feature = featureList[i]
  30. #如果是定性特征
  31. if mask_features[i]:
  32. if model.n_values == 'auto':
  33. #为属于第j个定性特征的每个有效值生成一个新节点,建立映射关系
  34. while k < n_activeFeatures and model.active_features_[k] < model.feature_indices_[j+1]:
  35. newFeature = Feature(feature.name)
  36. feature.transform('%s[%d]' % (model.__class__.__name__, model.active_features_[k] - model.feature_indices_[j]), newFeature)
  37. leaves = np.append(leaves, newFeature)
  38. k += 1
  39. else:
  40. #为属于第j个定性特征的每个有效值生成一个新节点,建立映射关系
  41. for k in range(model.feature_indices_[j]+1, model.feature_indices_[j+1]):
  42. newFeature = Feature(feature.name)
  43. feature.transform('%s[%d]' % (model.__class__.__name__, k - model.feature_indices_[j]), newFeature)
  44. leaves = np.append(leaves, newFeature)
  45. j += 1
  46. #如果不是定性特征,则直接根据原节点生成新节点
  47. else:
  48. newFeature = Feature(feature.name)
  49. feature.transform('%s[r]' % model.__class__.__name__, newFeature)
  50. leaves = append(leaves, newFeatures)
  51.  
  52. return leaves

4.3 多对多映射

  PCA类是典型的多对多映射的转换类,其提供了参数n_components_来表示转换后新特征的个数。之前说过降维的转换类,其既要求每一个原特征映射到所有新特征,也要求每一个新特征被所有原特征映射。故,我们设计处理PCA类的代码(many2many.py)如下:

  1. import numpy as np
  2. from sklearn.decomposition import PCA
  3. from feature import Feature
  4.  
  5. def doWithPCA(model, featureList):
  6. leaves = np.array([])
  7.  
  8. n_features = len(featureList)
  9.  
  10. #按照主成分数生成新节点
  11. for i in range(model.n_components_):
  12. newFeature = Feature(model.__class__.__name__)
  13. leaves = np.append(leaves, newFeature)
  14.  
  15. #为每一个原节点与每一个新节点建立映射
  16. for i in range(n_features):
  17. feature = featureList[i]
  18. for j in range(model.n_components_):
  19. newFeature = leaves[j]
  20. feature.transform(model.__class__.__name__, newFeature)
  21.  
  22. return leaves

5 实践

  到此,我们可以专注改进流水线处理和并行处理的模块了。为了不破坏Pipeline类和FeatureUnion类的核心功能,我们分别派生出两个类PipelineExt和FeatureUnionExt。其次,为这两个类增加私有方法getFeatureList,这个方法有只有一个参数featureList表示输入流水线处理或并行处理的特征列表(元素为feature.Feature类的对象),输出经过流水线处理或并行处理后的特征列表。设计内部方法_doWithModel,其被getFeatureList方法调用,其提供了一个公共的入口,将根据流水线上或者并行中的转换类的不同,具体调用不同的处理方法(这些不同的处理方法在one2one.py,one2many.py,many2many.py中定义)。在者,我们还需要一个initRoot方法来初始化网络结构,返回一个根节点。最后,我们尝试用networkx库读取自定义的网络结构,基于matplotlib的对网络进行图形化显示。以上部分的代码(ple.py)如下:

  1. from sklearn.feature_selection.base import SelectorMixin
  2. from sklearn.preprocessing import OneHotEncoder
  3. from sklearn.decomposition import PCA
  4. from sklearn.pipeline import Pipeline, FeatureUnion, _fit_one_transformer, _fit_transform_one, _transform_one
  5. from sklearn.externals.joblib import Parallel, delayed
  6. from scipy import sparse
  7. import numpy as np
  8. import networkx as nx
  9. from matplotlib import pyplot as plt
  10. from default import doWithDefault
  11. from one2one import doWithSelector
  12. from one2many import doWithOneHotEncoder
  13. from many2many import doWithPCA
  14. from feature import Feature
  15.  
  16. #派生Pipeline类
  17. class PipelineExt(Pipeline):
  18. def _pre_get_featues(self, featureList):
  19. leaves = featureList
  20. for name, transform in self.steps[:-1]:
  21. leaves = _doWithModel(transform, leaves)
  22. return leaves
  23.  
  24. #定义getFeatureList方法
  25. def getFeatureList(self, featureList):
  26. leaves = self._pre_get_featues(featureList)
  27. model = self.steps[-1][-1]
  28. if hasattr(model, 'fit_transform') or hasattr(model, 'transform'):
  29. leaves = _doWithModel(model, leaves)
  30. return leaves
  31.  
  32. #派生FeatureUnion类,该类不仅记录了转换行为,同时也支持部分数据处理
  33. class FeatureUnionExt(FeatureUnion):
  34. def __init__(self, transformer_list, idx_list, n_jobs=1, transformer_weights=None):
  35. self.idx_list = idx_list
  36. FeatureUnion.__init__(self, transformer_list=map(lambda trans:(trans[0], trans[1]), transformer_list), n_jobs=n_jobs, transformer_weights=transformer_weights)
  37.  
  38. def fit(self, X, y=None):
  39. transformer_idx_list = map(lambda trans, idx:(trans[0], trans[1], idx), self.transformer_list, self.idx_list)
  40. transformers = Parallel(n_jobs=self.n_jobs)(
  41. delayed(_fit_one_transformer)(trans, X[:,idx], y)
  42. for name, trans, idx in transformer_idx_list)
  43. self._update_transformer_list(transformers)
  44. return self
  45.  
  46. def fit_transform(self, X, y=None, **fit_params):
  47. transformer_idx_list = map(lambda trans, idx:(trans[0], trans[1], idx), self.transformer_list, self.idx_list)
  48. result = Parallel(n_jobs=self.n_jobs)(
  49. delayed(_fit_transform_one)(trans, name, X[:,idx], y,
  50. self.transformer_weights, **fit_params)
  51. for name, trans, idx in transformer_idx_list)
  52.  
  53. Xs, transformers = zip(*result)
  54. self._update_transformer_list(transformers)
  55. if any(sparse.issparse(f) for f in Xs):
  56. Xs = sparse.hstack(Xs).tocsr()
  57. else:
  58. Xs = np.hstack(Xs)
  59. return Xs
  60.  
  61. def transform(self, X):
  62. transformer_idx_list = map(lambda trans, idx:(trans[0], trans[1], idx), self.transformer_list, self.idx_list)
  63. Xs = Parallel(n_jobs=self.n_jobs)(
  64. delayed(_transform_one)(trans, name, X[:,idx], self.transformer_weights)
  65. for name, trans, idx in transformer_idx_list)
  66. if any(sparse.issparse(f) for f in Xs):
  67. Xs = sparse.hstack(Xs).tocsr()
  68. else:
  69. Xs = np.hstack(Xs)
  70. return Xs
  71.  
  72. #定义getFeatureList方法
  73. def getFeatureList(self, featureList):
  74. transformer_idx_list = map(lambda trans, idx:(trans[0], trans[1], idx), self.transformer_list, self.idx_list)
  75. leaves = np.array(Parallel(n_jobs=self.n_jobs)(
  76. delayed(_doWithModel)(trans, featureList[idx])
  77. for name, trans, idx in transformer_idx_list))
  78. leaves = np.hstack(leaves)
  79. return leaves
  80.  
  81. #定义为每个模型进行转换记录的总入口方法,该方法将根据不同的转换类调用不同的处理方法
  82. def _doWithModel(model, featureList):
  83. if isinstance(model, SelectorMixin):
  84. return doWithSelector(model, featureList)
  85. elif isinstance(model, OneHotEncoder):
  86. return doWithOneHotEncoder(model, featureList)
  87. elif isinstance(model, PCA):
  88. return doWithPCA(model, featureList)
  89. elif isinstance(model, FeatureUnionExt) or isinstance(model, PipelineExt):
  90. return model.getFeatureList(featureList)
  91. else:
  92. return doWithDefault(model, featureList)
  93.  
  94. #初始化网络的根节点,输入参数为原始特征的名称
  95. def initRoot(featureNameList):
  96. root = Feature('root')
  97. for featureName in featureNameList:
  98. newFeature = Feature(featureName)
  99. root.transform('init', newFeature)
  100. return root

  现在,我们需要验证一下成果了,不妨继续使用博文《使用sklearn优雅地进行数据挖掘》中提供的场景来进行测试:

  1. import numpy as np
  2. from sklearn.datasets import load_iris
  3. from sklearn.preprocessing import Imputer
  4. from sklearn.preprocessing import OneHotEncoder
  5. from sklearn.preprocessing import FunctionTransformer
  6. from sklearn.preprocessing import Binarizer
  7. from sklearn.preprocessing import MinMaxScaler
  8. from sklearn.feature_selection import SelectKBest
  9. from sklearn.feature_selection import chi2
  10. from sklearn.decomposition import PCA
  11. from sklearn.linear_model import LogisticRegression
  12. from sklearn.pipeline import Pipeline, FeatureUnion
  13. from ple import PipelineExt, FeatureUnionExt, initRoot
  14.  
  15. def datamining(iris, featureList):
  16. step1 = ('Imputer', Imputer())
  17. step2_1 = ('OneHotEncoder', OneHotEncoder(sparse=False))
  18. step2_2 = ('ToLog', FunctionTransformer(np.log1p))
  19. step2_3 = ('ToBinary', Binarizer())
  20. step2 = ('FeatureUnionExt', FeatureUnionExt(transformer_list=[step2_1, step2_2, step2_3], idx_list=[[0], [1, 2, 3], [4]]))
  21. step3 = ('MinMaxScaler', MinMaxScaler())
  22. step4 = ('SelectKBest', SelectKBest(chi2, k=3))
  23. step5 = ('PCA', PCA(n_components=2))
  24. step6 = ('LogisticRegression', LogisticRegression(penalty='l2'))
  25. pipeline = PipelineExt(steps=[step1, step2, step3, step4, step5, step6])
  26. pipeline.fit(iris.data, iris.target)
  27. #最终的特征列表
  28. leaves = pipeline.getFeatureList(featureList)
  29. #为最终的特征输出对应的系数
  30. for i in range(len(leaves)):
  31. print leaves[i], pipeline.steps[-1][-1].coef_[i]
  32.  
  33. def main():
  34. iris = load_iris()
  35. iris.data = np.hstack((np.random.choice([0, 1, 2], size=iris.data.shape[0]+1).reshape(-1,1), np.vstack((iris.data, np.full(4, np.nan).reshape(1,-1)))))
  36. iris.target = np.hstack((iris.target, np.array([np.median(iris.target)])))
  37. root = initRoot(['color', 'Sepal.Length', 'Sepal.Width', 'Petal.Length', 'Petal.Width'])
  38. featureList = np.array([transform.feature for transform in root.transformList])
  39.  
  40. datamining(iris, featureList)
  41.  
  42. root.printTree()
  43.  
  44. if __name__ == '__main__':
  45. main()

  运行程序,最终的特征及对应的系数输出如下:

  输出网络结构的深度遍历(部分截图):

  为了更好的展示转换行为构成的网络,我们还可以基于networkx构建有向图,通过matplotlib进行展示(ple.py):

  1. #递归的方式进行深度遍历,生成基于networkx的有向图
  2. def _draw(G, root, nodeLabelDict, edgeLabelDict):
  3. nodeLabelDict[root.label] = root.name
  4. for transform in root.transformList:
  5. G.add_edge(root.label, transform.feature.label)
  6. edgeLabelDict[(root.label, transform.feature.label)] = transform.label
  7. _draw(G, transform.feature, nodeLabelDict, edgeLabelDict)
  8.  
  9. #判断是否图是否存在环
  10. def _isCyclic(root, walked):
  11. if root in walked:
  12. return True
  13. else:
  14. walked.add(root)
  15. for transform in root.transformList:
  16. ret = _isCyclic(transform.feature, walked)
  17. if ret:
  18. return True
  19. walked.remove(root)
  20. return False
  21.  
  22. #广度遍历生成瀑布式布局
  23. def fall_layout(root, x_space=1, y_space=1):
  24. layout = {}
  25. if _isCyclic(root, set()):
  26. raise Exception('Graph is cyclic')
  27.  
  28. queue = [None, root]
  29. nodeDict = {}
  30. levelDict = {}
  31. level = 0
  32. while len(queue) > 0:
  33. head = queue.pop()
  34. if head is None:
  35. if len(queue) > 0:
  36. level += 1
  37. queue.insert(0, None)
  38. else:
  39. if head in nodeDict:
  40. levelDict[nodeDict[head]].remove(head)
  41. nodeDict[head] = level
  42. levelDict[level] = levelDict.get(level, []) + [head]
  43. for transform in head.transformList:
  44. queue.insert(0, transform.feature)
  45.  
  46. for level in levelDict.keys():
  47. nodeList = levelDict[level]
  48. n_nodes = len(nodeList)
  49. offset = - n_nodes / 2
  50. for i in range(n_nodes):
  51. layout[nodeList[i].label] = (level * x_space, (i + offset) * y_space)
  52.  
  53. return layout
  54.  
  55. def draw(root):
  56. G = nx.DiGraph()
  57. nodeLabelDict = {}
  58. edgeLabelDict = {}
  59.  
  60. _draw(G, root, nodeLabelDict, edgeLabelDict)
  61. #设定网络布局方式为瀑布式
  62. pos = fall_layout(root)
  63.  
  64. nx.draw_networkx_nodes(G,pos,node_size=100, node_color="white")
  65. nx.draw_networkx_edges(G,pos, width=1,alpha=0.5,edge_color='black')
  66. #设置网络中节点的标签内容及格式
  67. nx.draw_networkx_labels(G,pos,labels=nodeLabelDict, font_size=10,font_family='sans-serif')
  68. #设置网络中边的标签内容及格式
  69. nx.draw_networkx_edge_labels(G, pos, edgeLabelDict)
  70.  
  71. plt.show()

  以图形界面展示网络的结构:

6 总结

  聪明的读者你肯定发现了,记录下特征转换行为的最好时机其实是转换的同时。可惜的是,sklearn目前并不支持这样的功能。在本文中,我将这一功能集中到流水线处理和并行处理的模块当中,只能算是一个临时的手段,但聊胜于无吧。另外,本文也是抛砖引玉,还有其他的转换类,在原特征与新特征之间的映射关系上,百家争鸣。所以,我在Github上新建了个库,包含本文实例中所有的转换类处理的代码,在之后,我会慢慢地填这个坑,直到世界的尽头,抑或sklearn加入该功能。

7 参考资料

  1. 《使用sklearn优雅地进行数据挖掘》
  2. 《使用sklearn做单机特征工程》
  3. sklearn.preprocessing.OneHotEncoder

谁动了我的特征?——sklearn特征转换行为全记录的更多相关文章

  1. sklearn—特征工程

    sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...

  2. sklearn特征工程

    目录 一.    特征工程是什么?    2 ①特征使用方案    3 ②特征获取方案    4 ③特征处理    4 1.    特征清洗    4 2.    数据预处理    4 3.    特 ...

  3. MonkeyImage API 实践全记录

    1.    背景 鉴于网上使用MonkeyImage的实例除了方法sameAs外很难找到,所以本人把实践各个API的过程记录下来然自己有更感性的认识,也为往后的工作打下更好的基础.同时也和上一篇文章& ...

  4. 图像的全局特征--LBP特征

    原文链接:http://blog.csdn.net/zouxy09/article/details/7929531#comments 这个特征或许对三维图像特征提取有很大作用.文章有修改,如有疑问,请 ...

  5. 在CentOS6上配置MHA过程全记录

    在CentOS6上配置MHA过程全记录 MHA(Master High Availability)是一款开源的MariaDB or MySQL高可用程序,为MariaDB or MySQL主从复制架构 ...

  6. 在CentOS7上通过RPM安装实现LAMP+phpMyAdmin过程全记录

    在CentOS7上通过RPM安装实现LAMP+phpMyAdmin过程全记录 时间:2017年9月20日 一.软件环境: IP:192.168.1.71 Hostname:centos73-2.sur ...

  7. SAP S4HANA1610/Fiori安装过程全记录

    经历各种坑,从硬件到文件,终于安装成功. 有需要安装或使用S4HANA(含Fiori)的同学可以参考. 安装文件分享给大家 链接:http://pan.baidu.com/s/1mi7LfIS 密码: ...

  8. 机器学习进阶-图像特征sift-SIFT特征点 1.cv2.xfeatures2d.SIFT_create(实例化sift) 2. sift.detect(找出关键点) 3.cv2.drawKeypoints(画出关键点) 4.sift.compute(根据关键点计算sift向量)

    1. sift = cv2.xfeatures2d.SIFT_create() 实例化 参数说明:sift为实例化的sift函数 2. kp = sift.detect(gray, None)  找出 ...

  9. Express+Mongoose(MongoDB)+Vue2全栈微信商城项目全记录(二)

    用mogoose搭建restful测试接口 接着上一篇(Express+Mongoose(MongoDB)+Vue2全栈微信商城项目全记录(一))记录,今天单独搭建一个restful测试接口,和项目前 ...

随机推荐

  1. [测]jieba分词

    import jieba import os import jieba.analyse with open('src.txt', 'r') as file: data = file.read() se ...

  2. tomcat启动时 myeclipse控制台中文乱码

    情况1: tomcat中conf目录下有个叫server.xml的文件,里面 <Connector port="8080" protocol="HTTP/1.1&q ...

  3. Regex

    1. regex with variable example: find the number and put a parenthese around the number. output: a(52 ...

  4. The processing instruction target matching ''[xX][mM][lL]" is not allowed

    报错的来源是: <?xml version="1.0" encoding="UTF8"?> 解决方案::,一般是WSDL的头文件的格式出了问题,比如 ...

  5. 还是要好好研究开源的php

    听说facebook是php写的,还是要静下心来好好研究一番的嘛,踏踏实实点点滴滴的做起来!加油

  6. 在已有 Ubuntu 的基础上硬盘安装 Win7 实现双系统

    . . . . . LZ 的笔记本电脑一直安装的是 Ubuntu 系统,最近由于工作需要,要安装一个 Win7 系统.大家都知道,Linux 和 Windows 装双系统的时候要先装 Win 再装 L ...

  7. PTA Sort Three Distinct Keys

    Suppose you have an array of N elements, containing three distinct keys, "true", "fal ...

  8. 利用命令行将项目传到github上的简单操作

    (1)安装git后,打开cmd,进入要上传的文件夹中: (2)输入git init初始化本地git仓库: (3)git add .将所有文件提交到暂存区: (4)git commit -m'说明文字' ...

  9. 16.10.16学到的JAVA知识

    1. 每个字节就是八位,所以每个字节的取值范围是  -128~127,它可以保存一个英文字符,包括字母,数字和英文标点.而汉字的的数量很多,一个字节没法把所有的汉字表达出来,所以汉字就是用两个字节来存 ...

  10. mongkeyrunner实现循环随机输入值的方法

    from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice,MonkeyImagedevice= MonkeyRunner.waitF ...