编辑日志:
2021年8月27日:
2021年8月31日:
主要字段:采集ID(col_id)、采集年月(col_date)、是否有效(enable)、航空公司ID(airline_id)、机型系列ID(model_series_id)
主要字段:航空公司ID(airline_id)、航空公司全称(中文)(airline_cn_name)、航空公司代码(airline_code)
主要字段:机型系列ID(model_series_id)、机型(model)、系列(series)
主要字段:获取全部字段;有效字段: '机号'、 '日期'、 '是否为SDR'、 '运行种类'、 'ETOPS中大事件标识'、 'IFI'、 '故障类型'、 '故障ATA'、 '维修ATA'、 '问题描述'、 '排故措施'
select
ser_col_main.col_date,
sys_airline.airline_code,
sys_airline.airline_cn_name,
sys_model_series.model,
sys_model_series.series,
ser_col_fault.*
from
ser_col_main,
sys_airline,
sys_model_series,
ser_col_fault
where
(ser_col_main.enable='Enable')
and
(ser_col_main.col_id = ser_col_main.col_id(+))
and
(ser_col_main.airline_id = sys_airline.airline_id(+))
and
(ser_col_main.model_series_id = sys_model_series.model_series_id(+))
# 导入包
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import re
import jieba
import jieba.analyse as anls #关键词提取
import jieba.posseg as pseg
from gensim import corpora, models
from collections import Counter
import os
import time
from tqdm import tqdm_notebook
import tqdm
import re
import time
import copy
import random
import jieba
from collections import Counter
from sklearn.feature_extraction.text import TfidfVectorizer
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torch.optim import lr_scheduler
import matplotlib as mpl
mpl.rcParams['font.sans-serif'] = ['KaiTi', 'SimHei', 'FangSong'] # 汉字字体,优先使用楷体,如果找不到楷体,则使用黑体
mpl.rcParams['font.size'] = 12 # 字体大小
mpl.rcParams['axes.unicode_minus'] = False # 正常显示负号
# 导入数据
df = pd.read_csv("故障报告.csv",encoding = 'gbk')
# 查看首尾的3行数据
df.head(3).append(df.tail(3))
# 查看数据的列
print(df.columns)
print(len(df.columns))
可以看出,数据一共有30列
# 查看数据维度
df.shape
df.drop_duplicates().shape
说明数据没有重复值,有效行数为8873行
# 去除缺失值超过总数一半的列
col_to_use = []
for col in df.columns:
print("【{}】'的缺失值共有{}个".format(col,df[col].isna().sum()))
if df[col].isna().sum()< df.shape[0]*0.5:
col_to_use.append(col)
col_to_use
data = df[col_to_use]
data = data.dropna()
data.shape
可以看到更新后的数据有19列,非空行有8267行。
数据类型有 object
、int64
、float64
data.info()
现在我们对时间变量的数据类型做调整。
data['采集年月'] = data['采集年月'].astype('datetime64[ns]')
data['日期'] = data['日期'].astype('datetime64[ns]')
data.info()
sns.countplot(data['故障类型'])
可以看出,CL这一故障类型非常少,所以我们去掉它,这样故障类型就为 ML 和 PL。
data = data[data['故障类型']!='CL']
data = data.reset_index()
metadata = []
for i in data.columns:
# 定义数据角色
if i == '故障类型':
role = 'target'
elif i == 'index':
role = 'id'
else:
role = 'input'
# 定义数据层级
if data[i].unique().size == 2:
level = 'binary'
elif data[i].dtype == 'O':
level = 'nominal'
elif data[i].dtype == 'float64':
level = 'interval'
elif data[i].dtype == 'int64':
level = 'ordinal'
keep = True
if i == 'index':
keep = False
dtype = data[i].dtype
dic = {
'变量名': i,
'数据角色': role,
'数据层级': level,
'是否保留': keep,
'数据类型': dtype
}
metadata.append(dic)
meta = pd.DataFrame(metadata, columns = ['变量名','数据角色','数据层级','是否保留','数据类型'])
meta.set_index('变量名',inplace=True)
meta
# 筛选定类数据(nominal)
meta[(meta['数据层级'] == 'nominal') & (meta['是否保留'])].index
pd.DataFrame({'频数' : meta.groupby(['数据角色', '数据层级'])['数据角色'].size()}).reset_index()
可以发现我们的binary特征有2个,连续型特征有2个,定类特征有9个,定序特征有4个。
vars_with_missing = []
for f in data.columns:
missings = data[data[f] == -1][f].count()
if missings > 0 :
vars_with_missing.append(f)
missings_perc = missings/data.shape[0]
print('变量 {} 有 {} 条缺失值 ({:.2%})'.format(f, missings, missings_perc))
print('总计,有{}个变量有缺失值。'.format(len(vars_with_missing)))
v = meta[(meta['数据层级'] == 'nominal') & (meta['是否保留'])].index
for f in v:
dist_values = data[f].value_counts().shape[0]
print('变量 【{}】 有 【{}】 个唯一值'.format(f, dist_values))
可以看出,机型和ETOPS中大事件标识这两列,只有一个唯一值,那么可以去掉这两列。
问题描述 和 排故措施 由于是文本数据,暂时不考虑。
data = data.drop(['机型','ETOPS中大事件标识'],axis=1)
meta = meta.drop(['机型','ETOPS中大事件标识'])
data_copy = data.copy(deep=True)
data.info()
使用 jieba 对文本(这里指的是“问题描述”)数据进行分词和关键词提取,这里我们提取的信息针对于问题所发生(相关)的地理位置(省市),从而根据各特征分析该地区在此特征维度下的出现频率。
def get_key(data, flag_,top=10,w_ = 0.9, col = '问题描述'):
key = []
#jieba.load_userdict("dict.txt")
for problem in data[col]:
for x, w in anls.textrank(problem, withWeight=True):
#print('%s %s' % (x, w))
if w > w_:
words = pseg.lcut(x)
for word,flag in words:
if flag == flag_:
key.append(word)
return pd.Series(key).value_counts()[:100].index
get_key(data = data_copy, flag_ = 'v')
以上结果表明,我们在问题描述中出现频率最高的前100个动词中,筛选出下面10个有效关键词,比如:
等等。
一般的问题叙述规律是,发现有XXX问题;有XXX问题,显示XXX故障,XXX缺失,XXX破损等情况。
def findkeyword(data, key, col = '问题描述'):
dic_ = {}
lst_ = []
for idx,txt in enumerate(data[col]):
if key in ['丢失','缺失','破损','正常']:
res = txt[0:(txt.rfind(key)+len(key))]
else:
res = txt[txt.rfind(key):]
if len(res) != 1:
lst_.append(data[['航司名称','机号','故障ATA']].iloc[idx,:].tolist()+[res])
dic_[key] = lst_
return pd.DataFrame(dic_[key], columns = ['航司','机号','ATA编号','包含【{}】的所有故障信息'.format(key)])
for key in ['发现','有','反映','显示','伴随','查看','完成','告警','丢失','缺失','破损']:
problem_df = findkeyword(data = data_copy, key = key)
for col in problem_df.columns[:-2]:
plt.figure(figsize= [20,8])
sns.countplot(x = col,data = problem_df,order = problem_df[col].value_counts().index)
plt.xticks(rotation = 45)
plt.title('各【{}】包含【{}】故障信息频数图'.format(col,key),fontsize=20)
plt.ylabel('频数', fontsize=18)
plt.xlabel(col, fontsize=18)
key_ns = get_key(data = data_copy, flag_='ns')
def key_groupby(key_,col):
dic = {}
for key in key_:
lst = []
for row in range(len(data)):
if str(key) in data.loc[row,'问题描述']:
lst.append(row)
dic[key] = lst
for key,item in dic.items():
print("【{}】==>【问题描述】包含关键词:【{}】 的个数如下:".format(col,key))
print('########################################################')
df = data[[str(col),'问题描述']].iloc[item,:].groupby(str(col)).count().sort_values(by = '问题描述',ascending=False).rename(columns={'问题描述':'出现频数'})
print(df)
print('########################################################')
key_groupby(key_ = key_ns, col = '机号')
可以看出,在最常出现问题的城市前十名中,机号 B-3321
出现频率极高,全部排名前六名之内,也就是说此机号在已出现问题的10大城市中,全部覆盖,并且排在所有机号的前六名。
机号 B-3322
在最易出现问题的城市前两名(成都,上饶)中在所有机号中排名第一,说明在上饶至成都的直达航线上,机号 B-3322
存在过许多问题。
2017-12-14 12月13日13时许,成都航空航EU6675班号的ARJ21飞机,经过2小时30分的飞行,平稳降落在上饶三清山机场。
key_groupby(key_ = key_ns,col = '航司名称')
在飞往各问题城市的航司中,成都航空占了绝对主导地位。
key_groupby(key_ = key_ns,col = '是否为SDR')
在排名前十的易出现问题的城市中,绝大部分不是SDR
key_groupby(key_ = key_ns,col = '故障类型')
key_groupby(key_ = key_ns,col = '故障ATA')
故障 ATA 345800 在易出现问题的城市前六名中,频繁出现,在所有的 ATA 中排名保持前三,在成都、长沙、济南三地排名第一。
通过对故障报告的数据可视化,可以更清晰地描述故障在各个特征维度下的发生频率。
首先,关注各个机号问题描述含有 “CAS” 信息的频数。可以看出机号 B-3387
关于 CAS 信息故障出现问题次数最多,B-651M
最少。
plt.figure(figsize= [20,8])
data_ = data[['机号','问题描述']][data[['机号','问题描述']]['问题描述'].str.contains('CAS')]
print('故障信息关于【CAS】共有 【{}】 条数据'.format(len(data_)))
sns.countplot(x="机号", data=data_,
order = data_['机号'].value_counts().index)
plt.xticks(rotation = 90)
plt.ylabel('频数', fontsize=18)
plt.xlabel('机号', fontsize=18)
plt.figure(figsize= [20,8])
data_ = data[['机号','问题描述']][data[['机号','问题描述']]['问题描述'].str.contains('CMS')]
print('故障信息关于【CMS】共有 【{}】 条数据'.format(len(data_)))
sns.countplot(x="机号", data=data_,
order = data_['机号'].value_counts().index)
plt.xticks(rotation = 90)
plt.ylabel('频数', fontsize=18)
plt.xlabel('机号', fontsize=18)
plt.figure(figsize= [20,8])
data_ = data[['机号','问题描述']][data[['机号','问题描述']]['问题描述'].str.contains('MPP')]
print('故障信息关于【MPP】共有 【{}】 条数据'.format(len(data_)))
sns.countplot(x="机号", data=data_,
order = data_['机号'].value_counts().index)
plt.xticks(rotation = 90)
plt.ylabel('频数', fontsize=18)
plt.xlabel('机号', fontsize=18)
v = meta[(meta['数据层级'] == 'nominal') & (meta['是否保留'])].index
for f in v[:-2]:
plt.figure()
fig, ax = plt.subplots(figsize=(20,10))
# Calculate the percentage of target=1 per category value
cat_perc = data[[f, '故障类型']].groupby([f],as_index=False).count()[:10]
cat_perc.sort_values(by='故障类型', ascending=False, inplace=True)
# Bar plot
# Order the bars descending on target mean
sns.barplot(ax=ax, x=f, y='故障类型', data=cat_perc, order=cat_perc[f])
plt.ylabel('频数', fontsize=18)
plt.xlabel(f, fontsize=18)
plt.tick_params(axis='both', which='major', labelsize=18)
plt.xticks(rotation = 45)
plt.show();
现在针对故障发生时间,做一些基础变换,从而完成对年度、季度以及每天的故障发生频率。
df
data['日'] = data['日期'].map(lambda x: x.day)
data['年'] = data['日期'].map(lambda x: x.year)
data['月'] = data['日期'].map(lambda x: x.month)
data.head(3)
problems_per_year = Counter(data['年'])
years = list(problems_per_year.keys())
problems_year = list(problems_per_year.values())
problems_per_day = Counter(data['日'])
days = list(problems_per_day.keys())
problems_day = list(problems_per_day.values())
problems_per_month = Counter(data['月'])
months = list(problems_per_month.keys())
problems_month = list(problems_per_month.values())
def get_season(month):
if month >= 3 and month <= 5:
return '春天'
elif month >= 6 and month <= 8:
return '夏天'
elif month >= 9 and month <= 11:
return '秋天'
else:
return '冬天'
data['季节'] = data['月'].apply(get_season)
problems_per_season = Counter(data['季节'])
seasons = list(problems_per_season.keys())
problems_season = list(problems_per_season.values())
plt.rcParams['font.sans-serif']=['SimHei']#显示中文标签
plt.rcParams['axes.unicode_minus']=False
sns.set_color_codes("pastel")
fig = plt.figure(figsize=(14, 10))
sub1 = fig.add_subplot(2,2,1)
sns.barplot(x=years, y=problems_year, color='g', ax=sub1)
sub1.set(ylabel="故障", xlabel="年", title="每年故障数")
plt.setp(sub1.patches, linewidth=0)
plt.setp(sub1.get_xticklabels(), rotation=0, fontsize=12)
sub0 = fig.add_subplot(2,2,2)
sns.barplot(x=months, y=problems_month, color='yellow', ax=sub0)
sub0.set(ylabel="故障", xlabel="月", title="每月故障数")
plt.setp(sub1.patches, linewidth=0)
plt.setp(sub1.get_xticklabels(), rotation=0, fontsize=12)
sub2 = fig.add_subplot(2,2,3)
sns.barplot(x=days, y=problems_day, color='r', ax=sub2)
sub2.set(ylabel="故障", xlabel="日", title="每日故障数")
sub3 = fig.add_subplot(2,2,4)
sns.barplot(x=seasons, y=problems_season, color='b', ax=sub3)
texts = sub3.set(ylabel="故障", xlabel="季度", title="每季度故障数")
plt.tight_layout(w_pad=4, h_pad=3)
可以看出故障的发生时间主要集中在2019年和2021年,2018年之前发生的情况较少,也可能是未记录。
日频来看,每个月的24日故障发生频率最高,31日最低。
月频来看,12月到3月的故障发生频率全年占比最高。
季频来看,秋天故障发生频率最低,春季冬季频率较高,可能是极端天气造成。
基于故障类型对数据标注,使用自然语言处理(NLP)对故障描述(文本数据)进行文本预处理、信息检索、数据挖掘以及建模。
通过清除非中文字符、去除停用词以及使用 jieba 对文本精确分词等步骤,处理原始数据,并且分割为训练集和测试集,为代入模型做准备。
from sklearn.model_selection import train_test_split
data['target'] = data['故障类型'].map(lambda x: 0 if x == 'ML' else 1)
problems = data['问题描述'].values
target = data.target.values
x_train, x_test, y_train, y_test, = train_test_split(
problems, target, test_size=0.2, random_state=0)
len(y_train), len(y_test), len(x_train), len(x_test)
# 清理非中文字符
def clean_str(line):
line.strip('\n')
line = re.sub(r"[^\u4e00-\u9fff]", "", line)
line = re.sub(
"[0-9a-zA-Z\-\s+\.\!\/_,$%^*\(\)\+(+\"\')]+|[+——!,。?、~@#¥%……&*()<>\[\]::★◆【】《》;;=??]+", "", line)
return line.strip()
# 加载停用词
with open('stopwords.txt',encoding='utf-8') as f:
stopwords = [line.strip('\n') for line in f.readlines()]
def cut(data, labels, stopwords):
result = []
new_labels = []
for index in tqdm.notebook.tqdm(range(len(data))):
comment = clean_str(data[index])
label = labels[index]
# 分词
seg_list = jieba.cut(comment, cut_all=False, HMM=True)
seg_list = [x.strip('\n') for x in seg_list if x not in stopwords and len(x) > 1]
if len(seg_list) > 1:
result.append(seg_list)
new_labels.append(label)
# 返回分词结果和对应的标签
return result, new_labels
# 分别对训练数据和测试数据分词
train_cut_result, train_labels = cut(x_train, y_train, stopwords)
test_cut_result, test_labels = cut(x_test, y_test, stopwords)
使用 TF-IDF 进行信息检索与数据挖掘。
TF-IDF(term frequency–inverse document frequency)是一种用于信息检索与数据挖掘的常用加权技术。TF是词频(Term Frequency),IDF是逆文本频率指数(Inverse Document Frequency)。
一个词语在一篇文章中出现次数越多, 同时在所有文档中出现次数越少, 越能够代表该文章。
# TfidfVectorizer 传入原始文本
train_data = [' '.join(x) for x in train_cut_result]
test_data = [' '.join(x) for x in test_cut_result]
n_dim = 2000
# 数据的TF-IDF信息计算
# sublinear_tf=True 时生成一个近似高斯分布的特征,可以提高大概1~2个百分点
vectorizer = TfidfVectorizer(
max_features=n_dim, smooth_idf=True, sublinear_tf=True)
# 对训练数据训练
train_vec_data = vectorizer.fit_transform(train_data)
# 训练完成之后对测试数据转换
test_vec_data = vectorizer.transform(test_data)
vectorizer.get_feature_names()[:10]
# 输出的类别为 2
n_categories = 2
# 学习率过大会导致 loss 震荡
learning_rate = 0.001
# 损失函数
criterion = nn.CrossEntropyLoss()
# 迭代次数
epochs = 10
# 每次迭代同时加载的个数
batch_size = 100
class TxtDataset(Dataset):
def __init__(self, VectData, labels):
# 传入初始数据,特征向量和标签
self.VectData = VectData
self.labels = labels
def __getitem__(self, index):
# DataLoader 会根据 index 获取数据
# toarray() 是因为 VectData 是一个稀疏矩阵,如果直接使用 VectData.toarray() 占用内存太大,勿尝试
return self.VectData[index].toarray(), self.labels[index]
def __len__(self):
return len(self.labels)
# 线下内存足够大可以考虑增大 num_workers,并行读取数据
# 加载训练数据集
train_dataset = TxtDataset(train_vec_data, train_labels)
train_dataloader = DataLoader(train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=0
)
# 加载测试数据集
test_dataset = TxtDataset(test_vec_data, test_labels)
test_dataloader = DataLoader(test_dataset,
batch_size=batch_size,
shuffle=False,
num_workers=0
)
使用“问题描述”文本数据向量化后作为模型输入,故障类型作为输出,也就是标注。
故障类型分为 乘客发现
和 机组发现
两类,那么对于服务方来说,如果故障由乘客发现,那么故障多应发生在飞机飞行时,虽然危险程度不高,但是乘客的满意度会下降,对航司产生不良影响;如果由机组发现,那么一般出现在飞机未飞行并处在检查阶段时,严重程度较高,但是并非乘客发现,所以影响程度不大。
那么我们目的是,通过问题描述的文本数据关于故障类型建模,从而基于给定的故障描述,对其故障类型、影响程度以及严重程度进行预测和分析。
使用 PyTorch 构建神经网络模型,对训练集进行分批次训练,在迭代10次后,在测试集上的准确率平均达到91%。
class TxtModel(nn.Module):
def __init__(self, input_size, output_size):
super(TxtModel, self).__init__()
self.classifier = nn.Sequential(
nn.Linear(input_size, 256),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(256, 256),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(256, 128),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(128, 64),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(64, output_size)
)
def forward(self, x):
output = self.classifier(x.double())
return output.squeeze(1)
# 定义模型和优化器
model = TxtModel(n_dim, 2)
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
# 每两代衰减学习率
exp_lr_scheduler = lr_scheduler.StepLR(
optimizer, step_size=int(epochs/2), gamma=0.1)
model = model.double()
# 保存准确度最高的模型
best_model = copy.deepcopy(model)
best_accuracy = 0.0
for epoch in range(epochs):
exp_lr_scheduler.step()
model.train()
loss_total = 0
st = time.time()
# train_dataloader 加载数据集
for data, label in tqdm_notebook(train_dataloader):
output = model(data)
# 计算损失
loss = criterion(output, label)
optimizer.zero_grad()
# 反向传播
loss.backward()
optimizer.step()
loss_total += loss.item()
# 输出损失、训练时间等
print('epoch {}/{}:'.format(epoch+1, epochs))
print('training loss: {}, time resumed {}s'.format(
loss_total/len(train_dataset), time.time()-st))
model.eval()
loss_total = 0
st = time.time()
correct = 0
for data, label in test_dataloader:
output = model(data)
loss = criterion(output, label)
loss_total += loss.item()
_, predicted = torch.max(output.data, 1)
correct += (predicted == label).sum().item()
# 如果准确度取得最高,则保存准确度最高的模型
if correct/len(test_dataset) > best_accuracy:
best_model = copy.deepcopy(model)
print('testing loss: {}, time resumed {}s, accuracy: {}'.format(
loss_total/len(test_dataset), time.time()-st, correct/len(test_dataset)))
通过对数据的全面了解、深度挖掘以及建模,从多维度、多层次来解析机队可靠性故障报告文本数据;探究故障报告基于相关因子的分布和特征;利用神经网络、机器学习算法对文本数据关于故障类型建模,预测问题描述的故障类型。
不过,训练集的数据仅有6607条(较少),随着时间的推进,更多的数据涌入,会进一步提升模型的表现。并且需要在进一步对问题描述和排故措施的相关性和依赖程度进行探究,并且研究排故措施对于问题解决的执行效率和完成程度。
get_key(data = data_copy,flag_ = 'v',col = '排故措施')
for key in ['更换','跳开关','操作','保留','参考','判断','执行','防冰','清除','完成','控制']:
problem_df = findkeyword(data = data_copy, key = key,col = '排故措施')
for col in problem_df.columns[:-2]:
plt.figure(figsize= [20,8])
sns.countplot(x = col,data = problem_df,order = problem_df[col].value_counts().index)
plt.xticks(rotation = 45)
plt.title('各【{}】包含【{}】排故措施频数图'.format(col,key),fontsize=20)
plt.ylabel('频数', fontsize=18)
plt.xlabel(col, fontsize=18)
核心:通过余弦相似度分析故障问题描述和排故措施的相关程度。
余弦相似度就是通过一个向量空间中两个向量夹角的余弦值作为衡量两个个体之间差异的大小。 余弦相似度的值在0到1之间,越大越说明二者相关程度高。
通过分析文本相似度,可以说明针对故障问题描述,对应的排故措施是否有效实施并且对症下药。
把相似度分为 低中高
三等,其中
低
中
高
def stopwordslist(filepath):
stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]
return stopwords
# 加载停用词
stopwords = stopwordslist("stopwords.txt")
def cosine_similarity(sentence1: str, sentence2: str) -> float:
"""
:param sentence1: s
:param sentence2:
:return: 两句文本的相识度
"""
jieba.load_userdict("dict.txt")
seg1 = [word for word in jieba.cut(sentence1) if word not in stopwords]
seg2 = [word for word in jieba.cut(sentence2) if word not in stopwords]
word_list = list(set([word for word in seg1 + seg2]))#建立词库
word_count_vec_1 = []
word_count_vec_2 = []
for word in word_list:
word_count_vec_1.append(seg1.count(word))#文本1统计在词典里出现词的次数
word_count_vec_2.append(seg2.count(word))#文本2统计在词典里出现词的次数
vec_1 = np.array(word_count_vec_1)
vec_2 = np.array(word_count_vec_2)
#余弦公式
num = vec_1.dot(vec_2.T)
denom = np.linalg.norm(vec_1) * np.linalg.norm(vec_2)
cos = num / denom
sim = 0.5 + 0.5 * cos
return sim
idx_low = []
idx_mid = []
idx_high = []
data_copy = data_copy.astype(str)
for i in tqdm.notebook.tqdm(range(len(data_copy))):
sim = cosine_similarity(data_copy.loc[i,'问题描述'],data_copy.loc[i,'排故措施'])
if sim > 0.75:
idx_high.append(i)
elif sim < 0.75 and sim > 0.5:
idx_mid.append(i)
else:
idx_low.append(i)
data_low = data_copy.loc[idx_low,['航司名称','机号','问题描述','排故措施']]
data_mid = data_copy.loc[idx_mid,['航司名称','机号','问题描述','排故措施']]
data_high = data_copy.loc[idx_high,['航司名称','机号','问题描述','排故措施']]
print("故障描述与排故措施文本相似度为【低】的共有【{}】条".format(data_low.shape[0]))
print("故障描述与排故措施文本相似度为【中】的共有【{}】条".format(data_mid.shape[0]))
print("故障描述与排故措施文本相似度为【高】的共有【{}】条".format(data_high.shape[0]))
由于飞机故障的专业名词较多,造成普通分词方法会造成歧义,所以通过自定义词典,加入专业词语和专业名词。
比如:AMS信息、飞控不派遣、WATER FAULT、CVR/RIPS FAULT等
# 清理非中文字符
def clean_str(line):
line.strip('\n')
#line = re.sub(r"[^\u4e00-\u9fff]", "", line)
#line = re.sub(
# "[0-9a-zA-Z\-\s+\.\!\/_,$%^*\(\)\+(+\"\')]+|[+——!,。?、~@#¥%……&*()<>\[\]::★◆【】《》;;=??]+", "", line)
return line.strip()
# 加载停用词
with open('stopwords.txt',encoding='utf-8') as f:
stopwords = [line.strip('\n') for line in f.readlines()]
def cut(data, stopwords):
result = []
new_labels = []
jieba.load_userdict("dict.txt")
for index in tqdm.notebook.tqdm(range(len(data))):
comment = clean_str(data[index])
# 分词
#jieba.load_userdict("dict.txt")
seg_list = jieba.cut(comment, cut_all=False, HMM=True)
seg_list = [x.strip('\n') for x in seg_list if x not in stopwords and len(x) > 1]
if len(seg_list) > 1:
result.append(seg_list)
# 返回分词结果和对应的标签
return result
def flat(nums):
res = []
for i in nums:
if isinstance(i, list):
res.extend(flat(i))
else:
res.append(i)
return res
res = flat(cut(data_copy['问题描述'],stopwords))
res
plt.figure(figsize= [30,20])
data_ = pd.DataFrame(Counter(res).most_common(),columns = ['关键词','频数'])[:100]
print('故障信息关键词(中英文)共有 【{}】 条数据(自定义词典)'.format(len(pd.DataFrame(Counter(res).most_common(),columns = ['关键词','频数']))))
sns.barplot(x = '关键词',y = '频数', data = data_)
plt.xticks(rotation = 90, fontsize = 15)
plt.ylabel('频数', fontsize=22)
plt.xlabel('关键词', fontsize=22)
plt.title('故障信息关键词(中英文)频数图(自定义词典)Top 100',fontsize = 30)
def find_unchinese(file):
pattern = re.compile(r'[\u4e00-\u9fa5]')
unchinese = re.sub(pattern,"",file)
unchinese = re.sub(
"[-\s+\.\!\_,$%^*\(\)\+(+\"\'“”)]+|[+——!,。?、~@#¥%……&*()<>\[\]::★◆【】《》;;=??]+", " ", unchinese)
if len(unchinese) > 1:
return unchinese
def unchinese_freq(data,col,keyword,topK):
tmp = data[col].apply(find_unchinese).dropna()
tmp = tmp[tmp.str.contains(keyword)]
list_ = []
for txt in tmp:
list_.append(txt[:txt.rfind(keyword)+len(keyword)])
return pd.DataFrame(Counter(list_).most_common(),columns = ['英文关键词','频数'])[:topK]
unchinese_freq(data = data_copy, col = '问题描述', topK = 20, keyword = 'FAULT')
unchinese_freq(data = data_copy, col = '排故措施', topK = 20, keyword = 'FAULT')