前言

2016年6月,国务院办公厅印发《国务院办公厅关于促进和规范健康医疗大数据应用发展的指导意见》,文件指出健康医疗大数据应用发展将带来健康医疗模式的深刻变化,有利于提升健康医疗服务效率和质量。

一、赛题

赛题以心电图数据为背景,要求选手根据心电图感应数据预测心跳信号,其中心跳信号对应正常病例以及受不同心律不齐和心肌梗塞影响的病例,这是一个多分类的问题。通过这道赛题来引导大家了解医疗大数据的应用,帮助竞赛新人进行自我练习、自我提高。

1.1 赛题概况

比赛要求参赛选手根据给定的数据集,建立模型,预测不同的心跳信号。赛题以预测心电图心跳信号类别为任务,数据集可在文末下载,该数据来自某平台心电图数据记录,总数据量超过 20 20 20 万,主要为 1 1 1 列心跳信号序列数据,其中每个样本的信号序列采样频次一致,长度相等。为了保证比赛的公平性,将会从中抽取 10 10 10 万条作为训练集, 2 2 2 万条作为测试集 A, 2 2 2 万条作为测试集 B,同时会对心跳信号类别(label)信息进行脱敏。

1.2 数据概况

一般而言,对于数据在比赛界面都有对应的数据概况介绍(匿名特征除外),说明列的性质特征。了解列的性质会有助于我们对于数据的理解和后续分析。

Tip:匿名特征,就是未告知数据列所属的性质的特征列。

train.csv:

  • id 为心跳信号分配的唯一标识
  • heartbeat_signals 心跳信号序列(数据之间采用“,”进行分隔)
  • label 心跳信号类别(0、1、2、3)

testA.csv:

  • id 心跳信号分配的唯一标识
  • heartbeat_signals 心跳信号序列(数据之间采用“,”进行分隔)

1.3 预测指标

选手需提交 4 4 4 种不同心跳信号预测的概率,选手提交结果与实际心跳类型结果进行对比,求预测的概率与真实值差值的绝对值。

具体计算公式如下:

总共有 n n n 个病例,针对某一个信号,若真实值为 [ y 1 , y 2 , y 3 , y 4 ] [y1,y2,y3,y4] [y1,y2,y3,y4] ,模型预测概率值为 [ a 1 , a 2 , a 3 , a 4 ] [a1,a2,a3,a4] [a1,a2,a3,a4] ,那么该模型的评价指标 a b s − s u m abs-sum abssum
a b s − s u m = ∑ j = 1 n ∑ i = 1 4 ∣ y i − a i ∣ {abs-sum={\mathop{ \sum }\limits_{{j=1}}^{{n}}{{\mathop{ \sum }\limits_{{i=1}}^{{4}}{{ \left| {y\mathop{{}}\nolimits_{{i}}-a\mathop{{}}\nolimits_{{i}}} \right| }}}}}} abssum=j=1ni=14yiai

例如,某心跳信号类别为 1,通过编码转成 [ 0 , 1 , 0 , 0 ] [0,1,0,0] [0,1,0,0],预测不同心跳信号概率为 [ 0.1 , 0.7 , 0.1 , 0.1 ] [0.1,0.7,0.1,0.1] [0.1,0.7,0.1,0.1],那么这个信号预测结果的 a b s − s u m abs-sum abssum

a b s − s u m = ∣ 0.1 − 0 ∣ + ∣ 0.7 − 1 ∣ + ∣ 0.1 − 0 ∣ + ∣ 0.1 − 0 ∣ = 0.6 {abs-sum={ \left| {0.1-0} \right| }+{ \left| {0.7-1} \right| }+{ \left| {0.1-0} \right| }+{ \left| {0.1-0} \right| }=0.6} abssum=0.10+0.71+0.10+0.10=0.6

多分类算法常见的评估指标如下:

其实多分类的评价指标的计算方式与二分类完全一样,只不过我们计算的是针对于每一类来说的召回率、精确度、准确率和 F 1 F1 F1 分数。

1、混淆矩阵( C o n f u s e M a t r i x Confuse Matrix ConfuseMatrix

  • (1)若一个实例是正类,并且被预测为正类,即为真正类 T P ( T r u e P o s i t i v e ) TP(True Positive ) TP(TruePositive)
  • (2)若一个实例是正类,但是被预测为负类,即为假负类 F N ( F a l s e N e g a t i v e ) FN(False Negative ) FN(FalseNegative)
  • (3)若一个实例是负类,但是被预测为正类,即为假正类 F P ( F a l s e P o s i t i v e ) FP(False Positive ) FP(FalsePositive)
  • (4)若一个实例是负类,并且被预测为负类,即为真负类 T N ( T r u e N e g a t i v e ) TN(True Negative ) TN(TrueNegative

第一个字母 T / F T/F T/F,表示预测的正确与否;第二个字母 P / N P/N P/N,表示预测的结果为正例或者负例。如 T P TP TP 就表示预测对了,预测的结果是正例,那它的意思就是把正例预测为了正例。

2.准确率( A c c u r a c y Accuracy Accuracy

准确率是常用的一个评价指标,但是不适合样本不均衡的情况,医疗数据大部分都是样本不均衡数据。

3、精确率( P r e c i s i o n Precision Precision)也叫查准率简写为 P P P

精确率( P r e c i s i o n Precision Precision)是针对预测结果而言的,其含义是在被所有预测为正的样本中实际为正样本的概率在被所有预测为正的样本中实际为正样本的概率,精确率和准确率看上去有些类似,但是是两个完全不同的概念。精确率代表对正样本结果中的预测准确程度,准确率则代表整体的预测准确程度,包括正样本和负样本。

P r e c i s i o n = T P T P + F P Precision = \frac{TP}{TP + FP} Precision=TP+FPTP

4.召回率 R e c a l l Recall Recall) 也叫查全率 简写为 R R R

召回率(Recall)是针对原样本而言的,其含义是在实际为正的样本中被预测为正样本的概率。

R e c a l l = T P T P + F N Recall = \frac{TP}{TP + FN} Recall=TP+FNTP

下面我们通过一个简单例子来看看精确率和召回率。假设一共有 10 10 10 篇文章,里面 4 4 4 篇是你要找的。根据你的算法模型,你找到了 5 5 5 篇,但实际上在这 5 5 5 篇之中,只有 3 3 3 篇是你真正要找的。

那么算法的精确率是 3 ÷ 5 = 0.6 3{\div} 5=0.6 3÷5=0.6,也就是你找的这 5 5 5 篇,有 3 3 3 篇是真正对的。算法的召回率是 3 ÷ 4 = 0.75 3{\div} 4=0.75 3÷4=0.75,也就是需要找的 4 4 4 篇文章,你找到了其中三篇。以精确率还是以召回率作为评价指标,需要根据具体问题而定。

5.宏查准率( m a c r o − P macro-P macroP

计算每个样本的精确率然后求平均值

m a c r o P = 1 n ∑ 1 n p i {macroP=\frac{{1}}{{n}}{\mathop{ \sum }\limits_{{1}}^{{n}}{p\mathop{{}}\nolimits_{{i}}}}} macroP=n11npi

6.宏查全率( m a c r o − R macro-R macroR

计算每个样本的召回率然后求平均值

m a c r o R = 1 n ∑ 1 n R i {macroR=\frac{{1}}{{n}}{\mathop{ \sum }\limits_{{1}}^{{n}}{R\mathop{{}}\nolimits_{{i}}}}} macroR=n11nRi

7.宏 F 1 F1 F1(macro-F1)

m a c r o F 1 = 2 × m a c r o P × m a c r o R m a c r o P + m a c r o R {macroF1=\frac{{2 \times macroP \times macroR}}{{macroP+macroR}}} macroF1=macroP+macroR2×macroP×macroR

与上面的宏不同,微查准查全,先将多个混淆矩阵的 T P , F P , T N , F N TP,FP,TN,FN TP,FP,TN,FN 对应位置求平均,然后按照 P P P R R R 的公式求得 m i c r o − P micro-P microP m i c r o − R micro-R microR,最后根据 m i c r o − P micro-P microP m i c r o − R micro-R microR 求得 m i c r o − F 1 micro-F1 microF1

8.微查准率( m i c r o − P micro-P microP

m i c r o P = T P ‾ T P ‾ × F P ‾ {microP=\frac{{\overline{TP}}}{{\overline{TP} \times \overline{FP}}}} microP=TP×FPTP

9.微查全率( m i c r o − R micro-R microR

m i c r o R = T P ‾ T P ‾ × F N ‾ {microR=\frac{{\overline{TP}}}{{\overline{TP} \times \overline{FN}}}} microR=TP×FNTP

10.微 F 1 F1 F1 m i c r o − F 1 micro-F1 microF1

m i c r o F 1 = 2 × m i c r o P × m i c r o R m i c r o P + m i c r o R {microF1=\frac{{2 \times microP\times microR }}{{microP+microR}}} microF1=microP+microR2×microP×microR

1.4 赛题分析

  • 本题为传统的数据挖掘问题,通过数据科学以及机器学习深度学习的办法来进行建模得到结果;
  • 本题为典型的多分类问题,心跳信号一共有4个不同的类别;
  • 主要应用xgb、lgb、catboost,以及pandas、numpy、matplotlib、seabon、sklearn、keras等等数据挖掘常用库或者框架来进行数据挖掘任务。

二、代码示例

2.1 数据读取 (导入pandas)

#导包
import pandas as pd
import numpy as np

# 读取数据
path='./data/'
train_data=pd.read_csv(path+'train.csv')
test_data=pd.read_csv(path+'testA.csv')
print('Train data shape:',train_data.shape)
print('TestA data shape:',test_data.shape)
train_data.head() # 默认读取前5行的数据

2.2 分类指标计算示例

这里演示一下多分类估计指标的计算:

from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.metrics import f1_score
y_true    = [1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4,5,5,6,6,6,0,0,0,0] #真实值
y_pred = [1, 1, 1, 3, 3, 2, 2, 3, 3, 3, 4, 3, 4, 3,5,1,3,6,6,1,1,0,6] #预测值

#计算准确率
print("accuracy:", accuracy_score(y_true, y_pred))
#计算精确率
#计算macro_precision
print("macro_precision", precision_score(y_true, y_pred, average='macro'))
#计算micro_precision
print("micro_precision", precision_score(y_true, y_pred, average='micro'))
#计算召回率
#计算macro_recall
print("macro_recall", recall_score(y_true, y_pred, average='macro'))
#计算micro_recall
print("micro_recall", recall_score(y_true, y_pred, average='micro'))
#计算F1
#计算macro_f1
print("macro_f1", f1_score(y_true, y_pred, average='macro'))
#计算micro_f1
print("micro_f1", f1_score(y_true, y_pred, average='micro'))

结果:

accuracy: 0.5217391304347826
macro_precision 0.7023809523809524
micro_precision 0.5217391304347826
macro_recall 0.5261904761904762
micro_recall 0.5217391304347826
macro_f1 0.5441558441558441
micro_f1 0.5217391304347826
def abs_sum(y_pre,y_tru):
    #y_pre为预测概率矩阵
    #y_tru为真实类别矩阵
    y_pre=np.array(y_pre)
    y_tru=np.array(y_tru)
    loss=sum(sum(abs(y_pre-y_tru)))
    return loss
y_pre=[[0.1,0.1,0.7,0.1],[0.1,0.1,0.7,0.1]]
y_tru=[[0,0,1,0],[0,0,1,0]]
print(abs_sum(y_pre,y_tru))

结果:

1.2

2.3 baseline

  1. 导入第三方包:
# -*- coding: utf-8 -*-

#1.导入第三方包
import os
import gc
import math

import pandas as pd
import numpy as np

import lightgbm as lgb
import xgboost as xgb
from catboost import CatBoostRegressor
from sklearn.linear_model import SGDRegressor, LinearRegression, Ridge
from sklearn.preprocessing import MinMaxScaler

from sklearn.model_selection import StratifiedKFold, KFold
from sklearn.metrics import log_loss
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder

from tqdm import tqdm
import matplotlib.pyplot as plt
import time
import warnings
warnings.filterwarnings('ignore')
  1. 读取数据:
#2.读取数据
train = pd.read_csv('D:\\Cadabra_tools002\\tianqi_file\\train.csv')
test = pd.read_csv('D:\\Cadabra_tools002\\tianqi_file\\testA.csv')
train.head()# 默认读取前5行的数据

在这里插入图片描述

test.head()# 默认读取前5行的数据

在这里插入图片描述

  1. 数据预处理:
#3.数据预处理
def reduce_mem_usage(df):
    start_mem = df.memory_usage().sum() / 1024**2
    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))
    
    for col in df.columns:
        col_type = df[col].dtype
        
        if col_type != object:
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min > np.info(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)
            else:
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)
        else:
            df[col] = df[col].astype('category')
            
    end_mem = df.memory_usage().sum() / 1024**2
    print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))
    print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))
          
    return df

# 4.简单预处理
train_list = []

for items in train.values:
    train_list.append([items[0]] + [float(i) for i in items[1].split(',')] + [items[2]])

                               
train = pd.DataFrame(np.array(train_list))
train.columns = ['id'] + ['s_'+str(i) for i in range(len(train_list[0])-2)] + ['label']     
train = reduce_mem_usage(train)             

test_list = []

for items in test.values:
    test_list.append([items[0]]+[float(i) for i in items[1].split(',')])

test = pd.DataFrame(np.array(test_list))
test.columns = ['id'] + ['s_'+str(i) for i in range(len(test_list[0])-1)] 
test = reduce_mem_usage(test)         

在这里插入图片描述

  1. 训练数据/测试数据准备:
# 4.训练数据/测试数据准备

x_train = train.drop(['id','label'],axis=1)
y_train = train['label']
x_test = test.drop(['id'],axis=1)      
  1. 模型训练:
# 5.模型训练
def abs_sum(y_pre,y_tru):
    y_pre = np.array(y_pre)
    y_tru = np.array(y_tru)
    loss = sum(sum(abs(y_pre-y_tru)))
    return loss
def cv_model(clf, train_x, train_y, test_x, clf_name):
    folds = 5
    seed = 2021
    kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
    test = np.zeros((test_x.shape[0],4))
    
    cv_scores = []
    onehot_encoder = OneHotEncoder(sparse=False)
    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
        print('*****************************{}*************************'.format(str(i+1)))
        trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
        
        if clf_name == "lgb":
            train_matrix = clf.Dataset(trn_x, label=trn_y)
            valid_matrix = clf.Dataset(val_x, label=val_y)
            
            params = {
                'boosting_type': 'gbdt',
                'objective': 'multiclass',
                'num_class': 4,
                'num_leaves': 2 ** 5,
                'feature_fraction': 0.8,
                'bagging_fraction': 0.8,
                'bagging_freq': 4,
                'learning_rate': 0.1,
                'seed': seed,
                'nthread': 28,
                'n_jobs': 24,
                'verbose': -1,
            }
            
            model = clf.train(params,
                              train_set = train_matrix,
                              valid_sets = valid_matrix,
                              num_boost_round = 2000,
                              verbose_eval = 100,
                              early_stopping_rounds = 200)
            val_pred = model.predict(val_x, num_iteration=model.best_iteration) 
            test_pred = model.predict(test_x, num_iteration=model.best_iteration)
            
        val_y = np.array(val_y).reshape(-1, 1)
        val_y = onehot_encoder.fit_transform(val_y)
        print('预测的概率矩阵为:')
        print(test_pred)
        test += test_pred
        score = abs_sum(val_y, val_pred)
        cv_scores.append(score)
        print(cv_scores)
            
    print("%s_scotrainre_list:" % clf_name, cv_scores)
    print("%s_score_mean:" % clf_name, np.mean(cv_scores))
    print("%s_score_std:" % clf_name, np.std(cv_scores))
        
    test=test/kf.n_splits
        
    return test
def lgb_model(x_train, y_train, x_test):
    lgb_test = cv_model(lgb, x_train, y_train, x_test, "lgb")
    return lgb_test
lgb_test = lgb_model(x_train, y_train, x_test)  

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 预测结果:
# 6.预测结果
temp=pd.DataFrame(lgb_test)
result=pd.read_csv('D:\\Cadabra_tools002\\tianqi_file\\sample_submit.csv')
result['label_0']=temp[0]
result['label_1']=temp[1]
result['label_2']=temp[2]
result['label_3']=temp[3]
result.to_csv('D:\\Cadabra_tools002\\tianqi_file\\submit.csv',index=False)

最后会生成 submit.csv:
在这里插入图片描述
在这里插入图片描述


三、数据集下载

数据挖掘比赛数据集_心跳信号分类预测

在这里插入图片描述

Logo

永洪科技,致力于打造全球领先的数据技术厂商,具备从数据应用方案咨询、BI、AIGC智能分析、数字孪生、数据资产、数据治理、数据实施的端到端大数据价值服务能力。

更多推荐