这一部分所要讲的是,将收集到的数据进行专项处理,得到一个合理的数据格式,我们后面所要用的模型便能利用这样的数据,通过机器学习的方式进行数学建模。如此一来,挖掘到的数据就变得有价值了,通过这些数字表象找到了背后的意义。本项目是处理的是患者的心跳信号,意义便是建立起来的模型可以对某些病症进行检测。
目录
1.数据预处理
对心电特征进行行转列处理,同时为每个心电信号加入时间步特征time
train_heartbeat_df = data_train["heartbeat_signals"].str.split(",", expand=True).stack()
train_heartbeat_df = train_heartbeat_df.reset_index()
train_heartbeat_df = train_heartbeat_df.set_index("level_0")
train_heartbeat_df.index.name = None
train_heartbeat_df.rename(columns={"level_1":"time", 0:"heartbeat_signals"}, inplace=True)
train_heartbeat_df["heartbeat_signals"] = train_heartbeat_df["heartbeat_signals"].astype(float)
print(train_heartbeat_df)
简单分析一下以上的代码,主要利用pandas的一些处理方法:reset_index(),set_index()
1.1分离再组合
未进行处理以前的"heartbeat_signals"——
train_heartbeat_df = data_train["heartbeat_signals"].str.split(",", expand=True).stack()
print(train_heartbeat_df)
找到训练数据中"heartbeat_signals"中所对应的列数据,并利用split()函数以“ , ”,将每一行的数据分开,再利用stack()函数在axis=0这个维度对数据进行堆叠。对stack()的理解可以品读一下这位博主的见解——stack()的理解与使用
处理结果:
0 0 0.9912297987616655
1 0.9435330436439665
2 0.7646772997256593
3 0.6185708990212999
4 0.3796321642826237
...
99999 200 0.0
201 0.0
202 0.0
203 0.0
204 0.0
Length: 20500000, dtype: object
1.2 重置索引
这一部分将会使用reset_index()函数,我们可以通过这位博主简单的了解它的作用和使用方法——reset_index()的使用讲解
train_heartbeat_df = data_train["heartbeat_signals"].str.split(",", expand=True).stack()
train_heartbeat_df = train_heartbeat_df.reset_index()
print(train_heartbeat_df)
level_0 level_1 0
0 0 0 0.9912297987616655
1 0 1 0.9435330436439665
2 0 2 0.7646772997256593
3 0 3 0.6185708990212999
4 0 4 0.3796321642826237
... ... ... ...
20499995 99999 200 0.0
20499996 99999 201 0.0
20499997 99999 202 0.0
20499998 99999 203 0.0
20499999 99999 204 0.0
[20500000 rows x 3 columns]
1.3 弃用原有索引
将心跳信号的“level_0”所在列的数据作为新的索引,并弃用原有的索引。
train_heartbeat_df = data_train["heartbeat_signals"].str.split(",", expand=True).stack()
train_heartbeat_df = train_heartbeat_df.reset_index()
train_heartbeat_df = train_heartbeat_df.set_index("level_0")
print(train_heartbeat_df)
level_0 level_1 0
0 0 0.9912297987616655
0 1 0.9435330436439665
0 2 0.7646772997256593
0 3 0.6185708990212999
0 4 0.3796321642826237
... ... ...
99999 200 0.0
99999 201 0.0
99999 202 0.0
99999 203 0.0
99999 204 0.0
[20500000 rows x 2 columns]
1.4 改名
进行列名的更改,并对原表也进行相同的变动(1.3中打印的列名)
train_heartbeat_df = data_train["heartbeat_signals"].str.split(",", expand=True).stack()
train_heartbeat_df = train_heartbeat_df.reset_index()
train_heartbeat_df = train_heartbeat_df.set_index("level_0")
train_heartbeat_df.index.name = None
# inplace=True 表示该处理将会对原表也进行相应的处理;FALSE便是不对原表进行变动
train_heartbeat_df.rename(columns={"level_1": "time", 0: "heartbeat_signals"}, inplace=True)
train_heartbeat_df["heartbeat_signals"]=train_heartbeat_df["heartbeat_signals"].astype(float)
print(train_heartbeat_df)
time heartbeat_signals
0 0 0.991230
0 1 0.943533
0 2 0.764677
0 3 0.618571
0 4 0.379632
... ... ...
99999 200 0.000000
99999 201 0.000000
99999 202 0.000000
99999 203 0.000000
99999 204 0.000000
[20500000 rows x 2 columns]
1.5 心跳信号改后回填训练集
将处理后的心电特征加入到训练数据中,同时将训练数据label列单独存储。
data_train_label = data_train["label"]
data_train = data_train.drop("label", axis=1)
data_train = data_train.drop("heartbeat_signals", axis=1)
data_train = data_train.join(train_heartbeat_df)
print(train_heartbeat_df)
需要注意一下,这里显示的只是数据首尾的五个数据值,为ID=0 的前五个值和ID=9999 的后五个值
id time heartbeat_signals
0 0 0 0.991230
0 0 1 0.943533
0 0 2 0.764677
0 0 3 0.618571
0 0 4 0.379632
... ... ... ...
99999 99999 200 0.0
99999 99999 201 0.0
99999 99999 202 0.0
99999 99999 203 0.0
99999 99999 204 0.0
20500000 rows × 4 columns
data_train[data_train["id"]==1]
这里显示了某位就医者的心跳信号,共205个信号值
id time heartbeat_signals
1 1 0 0.971482
1 1 1 0.928969
1 1 2 0.572933
1 1 3 0.178457
1 1 4 0.122962
... ... ... ...
1 1 200 0.0
1 1 201 0.0
1 1 202 0.0
1 1 203 0.0
1 1 204 0.0
205 rows × 4 columns
2. tsfresh——时间序列特征处理
tsfresh可以自动计算大量的时间序列数据的特征,所谓的特征,即这些特征描述了时间序列的基本特征,如峰数、平均值或最大值或更复杂的特征,如时间反转对称统计。同时通过假设检验来将特征消减到最能解释趋势的特征,称为去相关性。然后,可以使用这些特征集在时间序列上构造统计或机器学习模型,例如在回归或分类任务中使用————引用知乎博主
2.1 pip安装
pip install tsfresh
2.2 特征处理
利用tsfresh包中的extract_features()函数对时间序列数据进行处理。
注意,该过程需要大内存以及好的计算性能来支撑。本人16g内存,在对原数据不进行压缩处理的情况下,内存瞬间占满爆炸,然后报错。
from tsfresh import extract_features
# 特征提取
train_features = extract_features(data_train, column_id='id', column_sort='time')
print(train_features)
id sum_values abs_energy mean_abs_change mean_change ...
0 38.927945 18.216197 0.019894 -0.004859 ...
1 19.445634 7.705092 0.019952 -0.004762 ...
2 21.192974 9.140423 0.009863 -0.004902 ...
... ... ... ... ... ...
99997 40.897057 16.412857 0.019470 -0.004538 ...
99998 42.333303 14.281281 0.017032 -0.004902 ...
99999 53.290117 21.637471 0.021870 -0.004539 ...
100000 rows × 779 columns
2.3 去掉处理过程中的NaN值
NaN可能为当前数据不支持此类特征的计算,所以在自动提取的过程中被置为了NaN值。
from tsfresh.utilities.dataframe_functions import impute
# 去除抽取特征中的NaN值
print(impute(train_features))
id sum_values abs_energy mean_abs_change mean_change ...
0 38.927945 18.216197 0.019894 -0.004859 ...
1 19.445634 7.705092 0.019952 -0.004762 ...
2 21.192974 9.140423 0.009863 -0.004902 ...
... ... ... ... ... ...
99997 40.897057 16.412857 0.019470 -0.004538 ...
99998 42.333303 14.281281 0.017032 -0.004902 ...
99999 53.290117 21.637471 0.021870 -0.004539 ...
100000 rows × 779 columns
2.4 特征选择
该过程是按照特征和响应变量之间的相关性进行特征选择,这一过程包含两步:首先单独计算每个特征和响应变量之间的相关性,然后利用Benjamini-Yekutieli procedure(如下) 进行特征选择,决定哪些特征可以被保留。
Benjamini, Y. and Yekutieli, D. (2001). The control of the false discovery rate in multiple testing under dependency. Annals of statistics, 1165–1188
from tsfresh import select_features
# 按照特征和数据label之间的相关性进行特征选择
train_features_filtered = select_features(train_features, data_train_label)
prin(train_features_filtered)
id sum_values fft_coefficient__attr_"abs"__coeff_35 fft_coefficient__attr_"abs"__coeff_34 ...
0 38.927945 1.168685 0.982133 ...
1 19.445634 1.460752 1.924501 ...
2 21.192974 1.787166 2.1469872 ...
... ... ... ... ...
99997 40.897057 1.190514 0.674603 ...
99998 42.333303 1.237608 1.325212 ...
99999 53.290117 0.154759 2.921164 ...
100000 rows × 700 columns
请注意 数据的shape,779列经过处理后留下了700列数据特征