【天池智慧海洋建设】Topline源码——特征工程学习
团队名称:天才海神号
链接:
https://github.com/fengdu78/tianchi_haiyang?spm=5176.12282029.0.0.5b97301792pLch
目录
前言
topline代码开源学习,仅关注特征工程部分,具体为输入,输出,作用、原理及部分个人理解。
I 数据部分
原始数据描述:
渔船ID:渔船的唯一识别,结果文件以此ID为标示
x: 渔船在平面坐标系的x轴坐标
y: 渔船在平面坐标系的y轴坐标
速度:渔船当前时刻航速,单位节
方向:渔船当前时刻航首向,单位度
time:数据上报时刻,单位月日 时:分
type:渔船label,三种作业类型(围网、刺网、拖网)
II 特征工程部分
方案优点
- 简单高效,过拟合风险低。
- 代码逻辑清晰简单,约200+行代码,可读性高,易于扩展和使用。
- 在百万量级数据,只提取了100+有效特征,全程运行时间只有16分钟左右。
- 特征工程中包含,时间,空间,速度,位移,相对值等各个维度的特征,全面且精简。
- 符合现实世界中的使用要求。
——天才海神号团队
2.1 分箱特征
分箱特征,距离海岸线的近似值。
对v求分箱特征,等分为200份,求每一份的统计值
对x求分箱特征,1000份和10000份,求每一份的次数统计值,和每一个分箱对应不同id数目
对y求分箱特征,1000份和10000份,求每一份的次数统计值,和每一个分箱对应不同id数目
求x,y分箱后的组合特征做为分组,求对应的次数统计值,和对应的id的不同数目 根据x分组,求y距离最小y的距离 # 可以理解为距离海岸线距离
根据y分组,求x距离最小x的距离 # 可以理解为距离海岸线距离
速度进行 200分位数分箱
df['v_bin'] = pd.qcut(df['v'], 200, duplicates='drop')
df['v_bin'] = df['v_bin'].map(dict(zip(df['v_bin'].unique(), range(df['v_bin'].nunique())))) # 分箱后映射编码
x,y位置分箱1000
for f in ['x', 'y']:
df[f + '_bin1'] = pd.qcut(df[f], 1000, duplicates='drop')
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数量映射
特征交叉x_bin1(2),y_bin1(2) 形成类别 统计每类数量映射到列
for i in [1, 2]:
df['x_y_bin{}'.format(i)] = df['x_bin{}'.format(i)].astype('str') + '_' + df['y_bin{}'.format(i)].astype('str')
df['x_y_bin{}'.format(i)] = df['x_y_bin{}'.format(i)].map(
dict(zip(df['x_y_bin{}'.format(i)].unique(), range(df['x_y_bin{}'.format(i)].nunique())))
)
df['x_bin{}_y_bin{}_count'.format(i, i)] = df['x_y_bin{}'.format(i)].map(df['x_y_bin{}'.format(i)].value_counts())
统计x_bin1 y_bin1的最大最小值
for stat in ['max', 'min']:
df['x_y_{}'.format(stat)] = df['y'] - df.groupby('x_bin1')['y'].transform(stat)
df['y_x_{}'.format(stat)] = df['x'] - df.groupby('y_bin1')['x'].transform(stat)
df.head()
2.2 间隔空间位移特征
根据id分组,对x求,上一个x,下一个x,间隔2个x的距离
根据id分组,对y求,上一个y,下一个y,间隔2个y的距离
根据上述距离,求上一时刻,下一时刻,间隔2个时刻的面积,相对值
g = df.groupby('id')
for f in ['x', 'y']:
#对x,y坐标进行时间平移 1 -1 2
df[f + '_prev_diff'] = df[f] - g[f].shift(1)
df[f + '_next_diff'] = df[f] - g[f].shift(-1)
df[f + '_prev_next_diff'] = g[f].shift(1) - g[f].shift(-1)
## 三角形求解上时刻1距离 下时刻-1距离 2距离
df['dist_move_prev'] = np.sqrt(np.square(df['x_prev_diff']) + np.square(df['y_prev_diff']))
df['dist_move_next'] = np.sqrt(np.square(df['x_next_diff']) + np.square(df['y_next_diff']))
df['dist_move_prev_next'] = np.sqrt(np.square(df['x_prev_next_diff']) + np.square(df['y_prev_next_diff']))
df['dist_move_prev_bin'] = pd.qcut(df['dist_move_prev'], 50, duplicates='drop')# 2时刻距离等频分箱50
df['dist_move_prev_bin'] = df['dist_move_prev_bin'].map(
dict(zip(df['dist_move_prev_bin'].unique(), range(df['dist_move_prev_bin'].nunique())))
) #上一时刻映射编码
2.3 空间位移的文本特征
空间位移的文本特征,提取Word2Vec,具有前后关系
根据id分组,以xy网格特征编号作为单词,求文本特征,Word2Vec,窗口大小为10,提取10维的特征
## 前后重复提除
def get_loc_list(x):
prev = ''
res = []
for loc in x:
loc = str(loc)
if loc != prev:
res.append(loc)
prev = loc
return res
## word2Vec
size = 10
sentence = df.groupby('id')['x_y_bin1'].agg(get_loc_list).tolist()
model = Word2Vec(sentence, size=size, window=20, min_count=1, sg=1, workers=12, iter=10)
emb = []
for w in df['x_y_bin1'].unique():
vec = [w]
try:
vec.extend(model[str(w)])
except:
vec.extend(np.ones(size) * -size)
emb.append(vec)
emb_df = pd.DataFrame(emb)
emb_cols = ['x_y_bin1']
for i in range(size):
emb_cols.append('x_y_bin1_emb_{}'.format(i))
emb_df.columns = emb_cols
emb_df.head()
同样的Word2vec方法使用,以’x_y_bin1’进行构造词向量
2.4 常见统计特征,相对值
根据v_bin和dist_move_prev_bin分组,求其他列的常见统计特征
'id': ['count'], 'x_bin1': [mode], 'y_bin1': [mode], 'x_bin2': [mode], 'y_bin2': [mode], 'x_y_bin1': [mode],
'x': ['mean', 'max', 'min', 'std', np.ptp, start, end],
'y': ['mean', 'max', 'min', 'std', np.ptp, start, end],
'v': ['mean', 'max', 'min', 'std', np.ptp], 'dir': ['mean'],
'x_bin1_count': ['mean'], 'y_bin1_count': ['mean', 'max', 'min'],
'x_bin2_count': ['mean', 'max', 'min'], 'y_bin2_count': ['mean', 'max', 'min'],
'x_bin1_y_bin1_count': ['mean', 'max', 'min'],
'dist_move_prev': ['mean', 'max', 'std', 'min', 'sum'],
'x_y_min': ['mean', 'min'], 'y_x_min': ['mean', 'min'],
'x_y_max': ['mean', 'min'], 'y_x_max': ['mean', 'min'],
def start(x):
try:
return x[0]
except:
return None
def end(x):
try:
return x[-1]
except:
return None
def mode(x):
try:
return pd.Series(x).value_counts().index[0]
except:
return None
df = df[df['flag'] == 1].reset_index(drop=True)
for f in ['dist_move_prev_bin', 'v_bin']:
# 上一时刻类别 速度类别映射处理
df[f + '_sen'] = df['id'].map(df.groupby('id')[f].agg(lambda x: ','.join(x.astype(str))))
# 一系列基本统计量特征 每列执行相应的操作
g = df.groupby('id').agg({
'id': ['count'], 'x_bin1': [mode], 'y_bin1': [mode], 'x_bin2': [mode], 'y_bin2': [mode], 'x_y_bin1': [mode],
'x': ['mean', 'max', 'min', 'std', np.ptp, start, end],
'y': ['mean', 'max', 'min', 'std', np.ptp, start, end],
'v': ['mean', 'max', 'min', 'std', np.ptp], 'dir': ['mean'],
'x_bin1_count': ['mean'], 'y_bin1_count': ['mean', 'max', 'min'],
'x_bin2_count': ['mean', 'max', 'min'], 'y_bin2_count': ['mean', 'max', 'min'],
'x_bin1_y_bin1_count': ['mean', 'max', 'min'],
'dist_move_prev': ['mean', 'max', 'std', 'min', 'sum'],
'x_y_min': ['mean', 'min'], 'y_x_min': ['mean', 'min'],
'x_y_max': ['mean', 'min'], 'y_x_max': ['mean', 'min'],
}).reset_index()
g.columns = ['_'.join(col).strip() for col in g.columns] #提取列名
g.rename(columns={'id_': 'id'}, inplace=True) #重命名id_
cols = [f for f in g.keys() if f != 'id'] #特征列名提取
g
2.5 行程特征
行程特征
总行程距离
每一步行程的占比
将'dist_move_prev_bin_sen', 'v_bin_sen'转化为onehot稀疏特征
df = df.drop_duplicates('id')[['id', 'label', 'dist_move_prev_bin_sen', 'v_bin_sen']].sort_values('id').reset_index(drop=True)
df = df.sort_values('label').reset_index(drop=True)# 去重以及排序
sub = df[df['label'] == -1].reset_index(drop=True)[['id']] #测试提交df
test_num = sub.shape[0]
labels = df[df['label'] != -1]['label'].values
df = df.merge(g, on='id', how='left') # 依据id合并特征
df[cols] = df[cols].astype('float32') # 特征列转换数据类型
df['dist_total'] = np.sqrt(np.square(df['x_end'] - df['x_start']) + np.square(df['y_end'] - df['y_start']))#总航海距离
df['dist_rate'] = df['dist_total'] / (df['dist_move_prev_sum'] + 1e-8) #总距离/id航行量
df = df.merge(emb_df, left_on='x_y_bin1_mode', right_on='x_y_bin1', how='left') #合并emb_df
df_values = sparse.csr_matrix(df[cols + emb_cols[1:] + ['dist_total', 'dist_rate']].values)
for f in ['dist_move_prev_bin_sen', 'v_bin_sen']:
cv = CountVectorizer(min_df=10).fit_transform(df[f].values)
df_values = sparse.hstack((df_values, cv), 'csr')
test_values, train_values = df_values[:test_num], df_values[test_num:]
# del df, df_values
gc.collect()
#总航海距离 特征是某一渔船xy区域的面积值