智慧海洋竞赛实践 专题三
文章目录
1. 特征工程概述
特征工程大体可分为3部分,特征构建、特征提取和特征选择。
1.1 特征构建
把现有特征进行组合或互相计算,得到新的特征。
1.2 特征提取和特征选择
- 特征提取
通过特征转换得到一组具有明显物理或统计意义的特征
- 特征选择(属性选择/变量选择)
从这些特征集合中选出一个子集。
2. 数据预处理
2.1 读入数据
参考链接:https://blog.csdn.net/weixin_43496331/article/details/115769140?spm=1001.2014.3001.5501
2.2 基本处理
读入数据(df)预览:
# 将中文名改为英文
df.rename(columns = {'渔船ID': 'id', '速度': 'v', '方向': 'dir'},inplace = True)
# 把分类变量数值化
df.rename(columns ={'type': 'label'},inplace = True) #修改变量名
df['label'] = df['label'].map({'拖网': 0, '围网': 1, '刺网': 2})
# 修改变量类型为float
cols = ['x','y','v']
for col in cols:
df[col] = df[col].astype('float')
df['dir'] = df['dir'].astype('int')
# 修改变量类型为时间类型,并增加年时间字段
df['time'] = pd.to_datetime(df['time'], format='%m%d %H:%M:%S')
df['date'] = df['time'].dt.date
df['hour'] = df['time'].dt.hour
df['month'] = df['time'].dt.month
df['weekday'] = df['time'].dt.weekday
3. 基础特征
3.1 距离计算
d = ( x − x 0 ) 2 + ( y − y 0 ) 2 2 d = \sqrt[2]{(x-x_0)^2+(y-y_0)^2} d=2(x−x0)2+(y−y0)2
3.2 时间划分
# 白天、黑天划分
df['day_nig'] = 0
df.loc[(df['hour'] > 5) & (df['hour'] < 20),'day_nig'] = 1
# 季度划分
df['quarter'] = 0
df.loc[(df['month'].isin([1, 2, 3])), 'quarter'] = 1
df.loc[(df['month'].isin([4, 5, 6, ])), 'quarter'] = 2
df.loc[(df['month'].isin([7, 8, 9])), 'quarter'] = 3
df.loc[(df['month'].isin([10, 11, 12])), 'quarter'] = 4
3.3 速度划分
定义速度划分区间,生成新变量‘v_cut’
def v_cut(v):
if v < 0.1:
return 0
elif v < 0.5:
return 1
elif v < 1:
return 2
elif v < 2.5:
return 3
elif v < 5:
return 4
elif v < 10:
return 5
elif v < 20:
return 5
else:
return 6
df['v_cut'] = df['v'].apply(lambda x:v_cut(x))
统计每个id的对应速度等级的个数,生成工具表c1
# 分组计算
tmp = df.groupby(['id', 'v_cut'], as_index=False)['v_cut'].agg({'v_cut_count': 'count'})
# 透视
c1 = tmp.pivot(index='id',columns='v_cut',values='v_cut_count')
# 修改列名
c1.columns = ['v_cut_' + str(col) for col in c1.columns.tolist()]
c1 = c1.reset_index()
3.4 方位划分
# 对方位进行16均分
df['d16'] = df['d'].apply(lambda x: int((x / 22.5) + 0.5) % 16 if not np.isnan(x) else np.nan)
# 分组
tmp = df.groupby(['id', 'd16'], as_index=False)['d16'].agg({'d16_count': 'count'})
# 透视
c3 = c3.pivot(index='ship', columns='d16', values='d16_count')
# 修改列名
c3.columns = ['d16_' + str(col) for col in c3.columns.tolist()]
c3 = c3.reset_index()
3.5 描述性统计
方法一:groupby().agg()
详细请参考:http://joyfulpandas.datawhale.club/Content/ch4.html
对速度不为0的数据进行描述性统计
# 统计速度为0的个数,以及速度不为0的统计量
df_zero_count = df.query("v==0")[['id', 'v']].groupby('id', as_index=False)['v'].agg({'num_zero_v': 'count'})
df_not_zero_agg = df.query("v!=0")[['ship', 'v']].groupby('id', as_index=False)['v'].agg(
{'v_max_drop_0': 'max',
'v_min_drop_0': 'min',
'v_mean_drop_0': 'mean',
'v_std_drop_0': 'std',
'v_median_drop_0': 'median',
'v_skew_drop_0': 'skew'})
c2 = df_zero_count.merge(df_not_zero_agg,on='id',how='left')
方法二:groupby().describe()
计算各种位数:[0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875]
df_raw.groupby('id')[key[i]].describe(percentiles=[0.05] + [ii / 1000 for ii in range(125, 1000, 125)] + [0.95])
3.6 坐标与角度偏移指标计算
方法:.shift()、.fillna(method=‘ffill’)
# 坐标
temp['ynext'] = temp.groupby('ship')['y'].shift(-1)
temp['ynext'] = temp['ynext'].fillna(method='ffill')
temp['xnext'] = temp.groupby('ship')['x'].shift(-1)
temp['xnext'] = temp['xnext'].fillna(method='ffill')
temp['angle_next'] = (temp['ynext'] - temp['y']) / (temp['xnext'] - temp['x'])
temp['angle_next'] = np.arctan(temp['angle_next']) / math.pi * 180
temp['angle_next_next'] = temp['angle_next'].shift(-1)
# 时间
temp['timenext'] = temp.groupby('ship')['time'].shift(-1)
temp['timediff'] = np.abs(temp['timenext'] - temp['time'])
temp['timediff'] = temp['timediff'].fillna(method='ffill')
temp['hc_xy'] = abs(temp['angle_next_next'] - temp['angle_next'])
3.7 分箱
方法:pd.qcut()、zip()、map()、transform()
# 速度
df['v_bin'] = pd.qcut(df['v'], 200, duplicates='drop') # 速度进行 200分位数分箱
df['v_bin'] = df['v_bin'].map(dict(zip(df['v_bin'].unique(),range(df['v_bin'].nunique()))))# 分箱后
# 坐标
for f in ['x', 'y']:
df[f + '_bin1'] = pd.qcut(df[f], 1000, duplicates='drop') # x,y位置分箱1000
df[f + '_bin1'] = df[f + '_bin1'].map(dict(zip(df[f + '_bin1'].unique(), range(df[f + '_bin1'].nunique()))))#编码
df[f + '_bin2'] = df[f] // 10000 # 取整操作
df[f + '_bin1_count'] = df[f + '_bin1'].map(df[f + '_bin1'].value_counts()) #x,y不同分箱的数量映射
df[f + '_bin2_count'] = df[f + '_bin2'].map(df[f + '_bin2'].value_counts()) #数量映射
df[f + '_bin1_id_nunique'] = df.groupby(f + '_bin1')['id'].transform('nunique')#基于分箱1 id数量映射
df[f + '_bin2_id_nunique'] = df.groupby(f + '_bin2')['id'].transform('nunique')#基于分箱2 id数量映射
pre_cols = df.columns
一开始一直在想为什么要把这个放到最开始,执行到最后,才发现是为了过滤出新增列的数据。
new_cols = [i for i in df.columns if i not in pre_cols]
df[new_cols].head()
通过字典的形式来构建方法和重命名
agg_dict = {}
for ag in ['max','mean','median','std','skew']:
agg_dict['{}_{}_{}'.format('speed',ag,"0")] = ag
print(agg_dict)
RUN:
{'speed_max_0': 'max', 'speed_mean_0': 'mean', 'speed_median_0': 'median', 'speed_std_0': 'std', 'speed_skew_0': 'skew'}
4. embedding特征
4.1 简介
word embedding就是把一个词用编码的方式表示以便于feed到网络中。Word Embedding有的时候也被称作为分布式语义模型或向量空间模型等,所以从名字和其转换的方式我们就可以明白, Word Embedding技术可以将相同类型的词归到一起,例如苹果,芒果香蕉等,在投影之后的向量空间距离就会更近,而书本,房子这些则会与苹果这些词的距离相对较远。
4.2 使用场景
Word Embedding可以用到特征生成,文件聚类,文本分类和自然语言处理等任务
如此次数据针对的航海问题中,对相同经纬度上不同的船进行Embedding,就可以得到每个船只的向量,就可以得到经常在某些区域工作的船只
4.3 Word2Vec
Word2vec在向量空间中对词进行表示, 或者说词以向量的形式表示,在词向量空间中:相似含义的单词一起出现,而不同的单词则位于很远的地方。这也被称为语义关系。神经网络不理解文本,而只理解数字。词嵌入提供了一种将文本转换为数字向量的方法。Word2vec就是在重建词的语言上下文。
**上下文:**在一般的生活情景中,当我们通过说话或写作来交流,其他人会试图找出句子的目的。例如,“印度的温度是多少”,这里的上下文是用户想知道“印度的温度”即上下文。
embedding_size=70
iters=70
min_count=3
window_size=25
num_runs=1
seed=9012
sentences = []
for i in boat_id:
traj = traj_data_corpus[traj_data_corpus['id']==i]
sentences.append(traj['no_bin'].values.tolist())
model = Word2Vec(sentences, size=embedding_size,
min_count=min_count,
workers=mp.cpu_count(),
window=window_size,
seed=seed, iter=iters, sg=0)
embedding_vec = []
for ind, seq in enumerate(sentences):
seq_vec, word_count = 0, 0
for word in seq:
if word not in model:
continue
else:
seq_vec += model[word]
word_count += 1
if word_count == 0:
embedding_vec.append(embedding_size * [0])
else:
embedding_vec.append(seq_vec / word_count)
4.4 NMF提取文本的主题分布
class nmf_list(object):
def __init__(self,data,by_name,to_list,nmf_n,top_n):
self.data = data
self.by_name = by_name
self.to_list = to_list
self.nmf_n = nmf_n
self.top_n = top_n
def run(self,tf_n):
df_all = self.data.groupby(self.by_name)[self.to_list].apply(lambda x :'|'.join(x)).reset_index()
self.data =df_all.copy()
print('bulid word_fre')
# 词频的构建
def word_fre(x):
word_dict = []
x = x.split('|')
docs = []
for doc in x:
doc = doc.split()
docs.append(doc)
word_dict.extend(doc)
word_dict = Counter(word_dict)
new_word_dict = {}
for key,value in word_dict.items():
new_word_dict[key] = [value,0]
del word_dict
del x
for doc in docs:
doc = Counter(doc)
for word in doc.keys():
new_word_dict[word][1] += 1
return new_word_dict
self.data['word_fre'] = self.data[self.to_list].apply(word_fre)
print('bulid top_' + str(self.top_n))
# 设定100个高频词
def top_100(word_dict):
return sorted(word_dict.items(),key = lambda x:(x[1][1],x[1][0]),reverse = True)[:self.top_n]
self.data['top_'+str(self.top_n)] = self.data['word_fre'].apply(top_100)
def top_100_word(word_list):
words = []
for i in word_list:
i = list(i)
words.append(i[0])
return words
self.data['top_'+str(self.top_n)+'_word'] = self.data['top_' + str(self.top_n)].apply(top_100_word)
# print('top_'+str(self.top_n)+'_word的shape')
print(self.data.shape)
word_list = []
for i in self.data['top_'+str(self.top_n)+'_word'].values:
word_list.extend(i)
word_list = Counter(word_list)
word_list = sorted(word_list.items(),key = lambda x:x[1],reverse = True)
user_fre = []
for i in word_list:
i = list(i)
user_fre.append(i[1]/self.data[self.by_name].nunique())
stop_words = []
for i,j in zip(word_list,user_fre):
if j>0.5:
i = list(i)
stop_words.append(i[0])
print('start title_feature')
# 讲融合后的taglist当作一句话进行文本处理
self.data['title_feature'] = self.data[self.to_list].apply(lambda x: x.split('|'))
self.data['title_feature'] = self.data['title_feature'].apply(lambda line: [w for w in line if w not in stop_words])
self.data['title_feature'] = self.data['title_feature'].apply(lambda x: ' '.join(x))
print('start NMF')
# 使用tfidf对元素进行处理
tfidf_vectorizer = TfidfVectorizer(ngram_range=(tf_n,tf_n))
tfidf = tfidf_vectorizer.fit_transform(self.data['title_feature'].values)
#使用nmf算法,提取文本的主题分布
text_nmf = NMF(n_components=self.nmf_n).fit_transform(tfidf)
# 整理并输出文件
name = [str(tf_n) + self.to_list + '_' +str(x) for x in range(1,self.nmf_n+1)]
tag_list = pd.DataFrame(text_nmf)
print(tag_list.shape)
tag_list.columns = name
tag_list[self.by_name] = self.data[self.by_name]
column_name = [self.by_name] + name
tag_list = tag_list[column_name]
return tag_list
data = df.copy()
data.rename(columns={'v':'speed','id':'ship'},inplace=True)
for j in range(1):
print('********* {} *******'.format(j))
for i in ['speed','x','y']:
data[i + '_str'] = data[i].astype(str)
nmf = nmf_list(data,'ship',i + '_str',8,2)
nmf_a = nmf.run(j)
nmf_a.rename(columns={'ship':'id'},inplace=True)
data_label = data_label.merge(nmf_a,on = 'id',how = 'left')
RUN: