大家好,我是小伍哥,最近事情太多了,有点忙,今天继续来写个策略挖掘的文章,时间有限,案例比较简单,我后面用更复杂的数据集更新下。
之前我写了决策树策略挖掘的文章,不过里面都是使用的数值特征,或者说是连续特征,如果我们是文本或者类别特征怎么办呢?能不能用决策树进行挖掘?
我最近研究了下,其实非连续的特征,也是可以用决策树的,挖掘处理的词组合或者行为组合,解释性更好。写个简单的案例,大家有需要的参考下。
我们先来构建一个简单的数据集,这里用的文本数据用的是我从邮箱里面复制出来的垃圾邮件数据,sep是包含空格。通过这个数据,我们尝试下怎么自动化的挖掘出策略组合。
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer,CountVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, auc, roc_auc_score
train = ['尽快,尽快,完成,工作',
'可,代开,各,行业,发票,sep',
'可,开,发票,请联系,sep',
'明天,一起,约个饭,sep',
'完成,的,非常,棒',
'项目,效果,很好,sep',
'有,发票,薇,sep',
'有,开票,微,sep,有',
'账单,请,查收',
'正规,增值税,发票,sep']
label=[0,1,1,0,0,0,1,1,0,1]
test = ['代开,增值税,发票,sep']
决策树只能处理数字型特征,那我们就要将文本型特征转换成数值,然后再还原回去。那怎么转换呢,我们首先想到的是one-hot编码,这样就比较简单了。我们使用sklearn.feature_extraction.text这个模块,但是这个有个问题,没有one-hot,我们转换下就行。
sklearn.feature_extraction.text.CountVectorizer这个函数,是每个样本中某个词的出现次数,其实我们只想要是否出现就行。
有了数据,我们要做的,就是对文本进行分词处理了
# 实例化
vectorizer_word = CountVectorizer(max_features=50,
token_pattern=r"(?u)\b\w+\b",
min_df=1,
analyzer='word',
#ngram_range=(2,5)
)
#训练进行
vectorizer_word.fit(train)
#词转换成CountVectorizer向量
tfidf_train = vectorizer_word.transform(train)
tfidf_test = vectorizer_word.transform(test)
# 可以看看我们的词典涨什么样
print(vectorizer_word.vocabulary_)
{'尽快': 8, '完成': 7, '工作': 9, '可': 4, '代开': 2, '各': 5, '行业': 23, '发票': 3, 'sep': 0, '开': 10, '请联系': 25, '明天': 15, '一起': 1, '约个饭': 21, '的': 20, '非常': 27, '棒': 18, '项目': 28, '效果': 14, '很好': 12, '有': 16, '薇': 22, '开票': 11, '微': 13, '账单': 26, '请': 24, '查收': 17, '正规': 19, '增值税': 6}
# 数据处理
import pandas as pd
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
pd.set_option('display.width', 200) # 设置打印宽度(**重要**)
X = pd.DataFrame(tfidf_train.toarray())
# 对大于1的数据全部转换成1
X[X>=1]=1
处理前
处理后,可以看到,我们的大于1的都变成1了
对我们的X重新命名,用词的名字进行命名
# 对字典进行反转
dic = {}
for k,v in vectorizer_word.vocabulary_.items():
dic[v] = k
# 对数据集进行命名
X.columns = [dic[i] for i in list(X.columns)]
下面就是命名完的表格了
下面是我们的规则提取函数构建,就是那个我之前写吐血的函数。
# 构建决策树规则提取函数
def XiaoWuGe_Get_Rules(clf,X):
n_nodes = clf.tree_.node_count
children_left = clf.tree_.children_left
children_right = clf.tree_.children_right
feature = clf.tree_.feature
threshold = clf.tree_.threshold
value = clf.tree_.value
node_depth = np.zeros(shape=n_nodes, dtype=np.int64)
is_leaves = np.zeros(shape=n_nodes, dtype=bool)
stack = [(0, 0)]
while len(stack) > 0:
node_id, depth = stack.pop()
node_depth[node_id] = depth
is_split_node = children_left[node_id] != children_right[node_id]
if is_split_node:
stack.append((children_left[node_id], depth+1))
stack.append((children_right[node_id], depth+1))
else:
is_leaves[node_id] = True
feature_name = [
X.columns[i] if i != _tree.TREE_UNDEFINED else "undefined!"
for i in clf.tree_.feature]
ways = []
depth = []
feat = []
nodes = []
rules = []
for i in range(n_nodes):
if is_leaves[i]:
while depth[-1] >= node_depth[i]:
depth.pop()
ways.pop()
feat.pop()
nodes.pop()
if children_left[i-1]==i:#当前节点是上一个节点的左节点,则是小于
a='{f}<={th}'.format(f=feat[-1],th=round(threshold[nodes[-1]],4))
ways[-1]=a
last =' & '.join(ways)+':'+str(value[i][0][0])+':'+str(value[i][0][1])
rules.append(last)
else:
a='{f}>{th}'.format(f=feat[-1],th=round(threshold[nodes[-1]],4))
ways[-1]=a
last = ' & '.join(ways)+':'+str(value[i][0][0])+':'+str(value[i][0][1])
rules.append(last)
else: #不是叶子节点 入栈
if i==0:
ways.append(round(threshold[i],4))
depth.append(node_depth[i])
feat.append(feature_name[i])
nodes.append(i)
else:
while depth[-1] >= node_depth[i]:
depth.pop()
ways.pop()
feat.pop()
nodes.pop()
if i==children_left[nodes[-1]]:
w='{f}<={th}'.format(f=feat[-1],th=round(threshold[nodes[-1]],4))
else:
w='{f}>{th}'.format(f=feat[-1],th=round(threshold[nodes[-1]],4))
ways[-1] = w
ways.append(round(threshold[i],4))
depth.append(node_depth[i])
feat.append(feature_name[i])
nodes.append(i)
return rules
模型构建和规则提取
from sklearn import tree
import numpy as np
from sklearn.tree import _tree
# 构建决策树
clf = tree.DecisionTreeClassifier(
max_depth=10,
)
clf = clf.fit(X, label)
# 规则提取
Rules = XiaoWuGe_Get_Rules(clf,X)
Rules
['发票<=0.5 & 开票<=0.5:5.0:0.0', '发票<=0.5 & 开票>0.5:0.0:1.0', '发票>0.5:0.0:4.0']
# 结果格式整理,整理成合格的表格形式
df = pd.DataFrame(Rules)
df.columns = ['allrules']
df['Rules'] = df['allrules'].str.split(':').str.get(0)
df['good'] = df['allrules'].str.split(':').str.get(1).astype(float)
df['bad'] = df['allrules'].str.split(':').str.get(2).astype(float)
df['all'] = df['bad']+df['good']
df['bad_rate'] = df['bad']/df['all']
# 结果排序&结果保存
df = df.sort_values(by='bad',ascending=False)
df.to_csv('df.csv',header=True)
这个就是我们挖掘的结果,我们可以看到,大于0.5的,就是包含这个词,小于0.5的,就是不包含,这样就可以指导我们进行规则配置了。
这里的文本,可以是行为,也可以是其他属性特征,只要转换成独立的、非连续的符号就行,比如sex_01,star_01等等。
有这个种非连续特征的挖掘的,可以参考下,我们浅浅的可视化一下,更好的可视化参考我之前的文章。 这个文章也需要更新了,里面的可视化的库已经变更了,并且没有做最后的策略的提取工作,有时间了一步步的优化,大家先将就看看。
import graphviz
dot_data = tree.export_graphviz(
clf,
out_file=None,
feature_names=X.columns,
class_names=['good','bad'],
filled=True, rounded=True,
special_characters=True)
graph = graphviz.Source(dot_data)
graph
如果有更复杂的,可视化的效果就会更好点,我们后面在搞。类似下面这种。也可以用dtreeviz这个包,更好看。