【天池智慧海洋建设】Topline源码——特征工程学习(天才海神号)

【天池智慧海洋建设】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区域的面积值

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿芒Aris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值