数据处理:当数据集以txt格式出现时
今天在对一个比赛数据集进行数据处理时发现,官方提供的数据集是txt文档形式,而我之前学习的教程所提供的案例中,均可以用json直接读取并划分数据集。这里记录下数据处理过程、遇到的困难和解决方法。
在该数据集中,数据的分布较工整,有规律可循。一条数据的格式如下:
id即数据序号
RNA碱基序列
LinearFold预测下的碱基成对情况
每一个碱基所对应的不成对概率(即output label)(多行)
可见,只需要按行读取就可以取得需要的数据,需要做的仅仅是建立若干个列表,在读到相应数据时将其存入。只是对于label需要用二维列表存储。
首先,导入库:
import paddle
from paddle.nn import Linear
import paddle.nn.functional as F
import os
import json
import random
import numpy as np
import re
并非所有的库都能用上,但先导进来吧:D
导入数据文件:
# 数据导入和数据集划分
train_data = open("训练集文件名","r")
val_data = open("验证集文件名","r")
test_data = open("测试集文件名","r")
print('data load down.')
接下来,要实现数据的提取,基本的思路是用正则表达式匹配每种内容独一无二的特点:
id一定以>id_开头;
RNA碱基由AUGC四种,所以开头字母只可能是其中一种;
碱基配对情况只能由 .() 构成,自然也只能由它们开头;
output label的形式是:碱基编号+空格+不成对概率。
于是可以在一次遍历下匹配完成,以id为例:
id = []
try:
for line in train_data:
#忽略行末的换行符
line=line.strip('\n')
#用正则表达式分别匹配需要的数据
if re.search("^>.*$", line):
id.append(str(line))
finally:
print('id pick up over')
其他可以如法炮制。但唯一不同的是label,因为它是一对多的,每个序列对应n个label,如果单一用append
加入一维列表最后,很不方便后续的操作,所以采用二维数组的方法。对于这个二维数组,由于每条数据所含的碱基数不同,为了防止index out of range
,我们可以实现在存储其他数据时进行初始化,这样保证创建的列表恰好不重不漏地覆盖所有的label。具体代码如下:
注意,这里id其实没有什么用,但我随后发现的一种改进方法用id很方便,所以保留对id的提取。
# 提取信息并加入数据集
id = []
train_sequences = []
train_structures = []
train_labels = []
amount = 4750
i,j = 0,0
try:
print('picking up classified data ... ... ... ...')
for line in train_data:
#忽略行末的换行符
line=line.strip('\n')
#用正则表达式分别匹配需要的数据
if re.search("^>.*$", line):
id.append(str(line))
if re.search("^[AUGC].*$", line):
train_sequences.append(str(line))
if re.search("^[.()].*$", line):
train_structures.append(str(line))
lb = list(range(len(str(line))))
train_labels.append(lb)
if re.search("^[0-9].*$", line):
train_labels[i][j] = str(re.sub('[0-9]* ','',line,count=0))
if j< len(train_labels[i])-1:
#j += 1
elif i< amount :
j = 0
i += 1
train_labels[a][b] = str(re.sub('[0-9]* ','',line,count=0))
#debug用下面这行代码:
#train_labels[a][b] = str(line)
finally:
print('id pick up over')
可以看出,我在提取配对情况时顺便用RNA的总长度初始化了数组,并在下一步用一个很简单的方法:遍历一个数据的label直到提取完全,之后index j归零,i自增一继续读取下一个数据的label。由于我们只想要不成对概率,不希望混杂了前面的碱基位置,所以使用`re.sub()删去碱基序号和空格。
到这里开始会让人感到奇怪:这中处理label的方式容易出错。而目前该方法生效的原因是:很凑巧,output label的个数和碱基长度相同,制作数据集的人又恰好没有遗漏;数据文件在我们手中又恰好安全,没有遗漏;读取文件时恰好又是从上往下一行行阅读。
就好像我要把一叠纸牌按固定的顺序发给在座的诸位牌友,我选择的方式是:从牌堆顶部抽取若干张张卡牌,交给给第一个人,再抽取若干张交给第二个,以此类推。虽然这种方法没有问题,但当你给4000多人发几万张排时,你可能还是希望能确切地指出:编号为[32,3]的牌是发给32号玩家的第三张牌。这样可以完全避免错误,给发牌的人更多的安全感。
所以我更新了处理label的方法:在读到一个数据时,我先从id中剪下数据序号,记为a;在存储label时,之前我们不想要的碱基编号,剪下来记为b,存取是直接将label存入[a-1][b-1]号位上。这是一种准确无误的方式:
# 提取id并加入数据集
id = []
train_sequences = []
train_structures = []
train_labels = []
amount = 4750
a,b = 0,0
try:
print('picking up classified data ... ... ... ...')
for line in train_data:
#忽略行末的换行符
line=line.strip('\n')
#用正则表达式分别匹配需要的数据
if re.search("^>.*$", line):
id.append(str(line))
a = int(line.replace(">id_",'')) - 1
if re.search("^[AUGC].*$", line):
train_sequences.append(str(line))
if re.search("^[.()].*$", line):
train_structures.append(str(line))
lb = list(range(len(str(line))))
train_labels.append(lb)
if re.search("^[0-9].*$", line):
b = int(re.findall('[0-9]* ',line)[0].replace(' ','')) - 1
#train_labels[a][b] = str(re.sub('[0-9]* ','',line,count=0))
#debug用下面这行代码:
train_labels[a][b] = str(line)
finally:
print('data pick up over.')
待更新......
不更了,搭了小网络跑出来loss高达0.8,也就数据处理能看