记录一下这段时间对生信数据处理后遇到的问题和处理的方法。问题在于数据提取之后的数据存在错误。我把自己没检查导致数据错误和检查过但没发现的数据错误的情况拿出来处刑,顺便提醒一下其他小白同志。说起这个我发现生信领域对于处理数据这部分的内容有很多相关的教程,但却缺乏如何评估处理后的数据的教程和方法论。在此记录一下,说不定未来的某天还是写下的log帮助了自己。
1、错误的形式及处理方法
正确只有一种,但错误却能千奇百怪
常见的错误
-
缺乏理解:在处理数据之前,没有对数据本身和业务需求进行足够的了解和分析。
- 这里需要与需求方进行不断的沟通,并且确认好提取的
数据类型
和数据字段
。
- 这里需要与需求方进行不断的沟通,并且确认好提取的
-
忽略异常值:异常值是指与其他数据点明显不同的数据点
- 数值类型的异常值可通过
统计学的方法
和可视化的方法
查找到。
- 数值类型的异常值可通过
-
忽略重复值
- 重复值是指数据集中出现多次的相同数据,重复值出现的地方不仅仅是
重复行
,也可能是同一个各种出现多个值,但重复值的有些时候是具有意义
的,所以删除前需要确认重复值是否具有意义。
e.g1 数据有重复的行
基因ID 基因名 ENSG123 F-box/LRR-repeat ENSG123 F-box/LRR-repeat e.g2 数据同一行中有重复的值,如下表所示:
基因ID 基因名 ENSG123 F-box/LRR-repeat/F-box解决方法pandas实现方式(一般使用set的功能对重复值进行去重) - 重复值是指数据集中出现多次的相同数据,重复值出现的地方不仅仅是
- Python pandas实现
#表格去重
df.drop_duplicates(inplace=True)
#例子2去重,不同的数据用不同的代码,不要刻舟求剑!
unique_name = list(set(df['基因名'].str.split('/')))
df['基因名'] = unique_name
-
数据类型错误:使用错误的数据类型可能会导致数据计算错误。
-
将文本数据视为数字数据;
-
原本为int的数据变成了float的类型;
-
- Python pandas转变数据类型
# pandas
import pandas as pd
# 读取数据前或构建dataframe前需要指定数据的类型
data = {'column1': [1, 2, 3], 'column2': ['4', '5', '6']}
df = pd.DataFrame(data, dtype={'column1': float, 'column2': int})
#转变dataframe的数据
df['column1'] = df['column1'].astype(float)
df['column2'] = df['column2'].astype(int)
#如果数据中有空值,需要预先填充空值
df['column1'] = df['column1'].fillna(0).astype(int)
#或者通过用apply和lambda函数跳过空值
df['column1'] = df['column1'].apply(lambda x: str(x) if x not np.nan else np.nan)
- 不考虑数据大小:在处理大型数据集时,需要考虑计算机的存储和内存限制。如果不考虑数据大小,可能会导致程序崩溃或运行缓慢。
- 没错,如果文件太大将会导致数据分析无法进行下去,因此可以使用分块的方式对数据进行处理(这篇会有专门的讨论在此不展开)。
- 忽略数据质量:传说中的garbage in garbage out!
- 如果是样品的数据不行,建议
重新提取样品测序
。
- 空值
-
有些时候数据库对数据的录入是有要求的,现实收集到的数据有用
空格
、None
、Na
、甚至是一些符号,因此处理控制的时候并不能根据已有的进行空值的填充,需要与需求方或者看自己需求填充空值。 -
python查找空值的方法,下面的两种方法会返回"True" or "False"
-
#查找空值
df.isnull()
df.isna()
#选择没有空值的子集
df[df.isnull() == False]
#选择某些列不为空值的操作
df[df['列名'].isna() == False]
df[df['列名'].isnull() == False]
- python填充空值的方法
import pandas as pd
#整表填充
df.fillna('填充的值', inplace=True) #这个是可以直接处理原表的数据
#只填充某一列的数据
df['column1'].fillna('填充的值', inplace=True)
#表中本来已经有表示空值的情况,避免pandas将空值转变成np.nan
df = pd.read_csv(data, na_values="指定的空值类型")
不常见但却经典的新手错误
-
程序错误(但能跑)
-
这里的程序错误并不是指,编译器或者IDE抛出的异常,而是指程序逻辑正确的情况下,但却因为错误将不应该合并进来的数据合并进来了(比如,
复制后还保留了原来的变量名
) -
缺乏相关领域的知识
-
分割符惹来的祸端
- 例如用awk提取gff文件时没有考虑分割符awk默认分割符(默认分隔符为空格)会
导致数据减少
,且看下面的例子,没有指定分割
符的情况下统计行数少了。这种情况并不容易察觉的,因为碰巧文件里面可能全部都是gene
而没有batch gene
这样的字段,使用wc -l
进行统计,会得到一样的行数
- 例如用awk提取gff文件时没有考虑分割符awk默认分割符(默认分隔符为空格)会
#gff基因数据如下
awk -v FS="\t" '$3~/gene/' demo.tsv|wc -l
>> 5
awk '$3~/gene/' demo.tsv|wc -l
>> 4
- 正则匹配的问题(这篇不展开讨论)
- 通配符,需要注意通配符是不是
只匹配到
目标内容
- 通配符,需要注意通配符是不是
2、如何科学地检查数据
- 抽样检查
- python实现方法
import pandas
# 读取数据集
df = pd.read_csv('data.csv')
# 从数据集中抽取指定数量的数据,随机选取 10 个样本
sample_df = df.sample(n=10)
# 从数据集中按比例抽取数据,从数据集中随机选取 20% 的样本
sample_df = df.sample(frac=0.2)
# 对数据集进行加权抽样
weights = [0.5, 0.3, 0.2] #定义样本权重
sample_df = df.sample(n=10, weights=weights, replace=True)
- linux实现抽样的方法:工具
shuf
# 从文件中抽取100行的数
shuf -n 100 输入文件 > 输出文件
- 与源数据或者样品的示例数据进行比较
- 工具:python-pandas; linux-uniq/diff/grep/awk/md5sum进行校验
- python的
equals
方法(对象是dataframe和序列)
# 在 pandas 库中,DataFrame 和 Series 对象都提供了 equals() 方法,用于比较两个对象的相等性。
import pandas as pd
# 创建两个 DataFrame 对象
df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
print(df1.equals(df2))
>>> True
- linux的方法:
-
diff
直接比较两个文件内容的差异:如果一致没有内容输出 - 通过md5sum校验的方法比较:通过生成校验码,查看校验码是否一致
-
#linux 方法
diff 文件1 文件2
# md5sum 校验
md5sum demo.tsv
7fad8aac8cbfaabf7f6a0b2c70c50b1d demo.tsv
md5sum demo2.tsv
7fad8aac8cbfaabf7f6a0b2c70c50b1d demo2.tsv
-
请其他人(上级,有经验的同学、同事)帮忙检查
为什么会说到这一点呢,新手会犯错很大一部分原因是因为对数据并不敏感,比如说基因可能在
某些物种里面是不可能存在的但数据格式是正确的
,这时候已经有可能是脚本出现错误了;类似的还有物种名字之间少了空格、sp后面少了'.',熟悉该领域的人一眼就能看出来。另外,旁观者清当局者迷
,找其他人帮忙看看可能会发现不一样的错误。
3、总结
要明白一点数据清洗的并不是一蹴而就的,多番来回才是常态
评价一下python pandas
-
坑点1
:python的pandas跟excel表格一样,会智能地转变数据类型,但这会给数据处理带来麻烦,所以一开始就要把数据类型指定好; -
坑点2
:合并两个表格的时候,如果其中一个表格的数据与另一个数据的行数不符合,比如说少了的数据会被空值填充,如果此时使用df.info()会看到原本已转换好int类型的列变成了float(请看官方的例子,在这里加上了df.info()查看数据类型),建议所有的数据类型都在最终的表格中再定义一次
;
df1 = pd.DataFrame({'a': ['foo', 'bar'], 'b': [1, 2]})
>>> df1
a b
0 foo 1
1 bar 2
>>> df1.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2 entries, 0 to 1
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 a 2 non-null object
1 b 2 non-null int64
df2 = pd.DataFrame({'a': ['foo', 'baz'], 'c': [3, 4]})
>>>df2
a c
0 foo 3
1 baz 4
>>> df2.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2 entries, 0 to 1
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 a 2 non-null object
1 c 2 non-null int64
dtypes: int64(1), object(1)
memory usage: 160.0+ bytess
#合并两张表后查看数据类型
df3 = df1.merge(df2, how='left', on='a')
a b c
0 foo 1 3.0
1 bar 2 NaN
>>> df3
a b c
0 foo 1 3.0
1 bar 2 NaN ##这里可以看到整数已经变成了浮点数
>>> df3.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2 entries, 0 to 1
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 a 2 non-null object
1 b 2 non-null int64
2 c 1 non-null float64
dtypes: float64(1), int64(1), object(1)
memory usage: 64.0+ bytes
-
坑点3
:pandas会自动识别表中已经用NA
字符串填充的空值,并且会将字符串“NA”转变成np.nan,遇到这种情况只能在导出数据前
用df.fillna的功能再次填充空值了; - linux的工具没有直接提供查看空值的方法,观察除了靠肉眼以外,还依靠推测各种常用的分隔符。在Linux的
Vim编辑器
能够通过设置set list
查看文本中的分割符,或使用cat -A file(mac环境为 cat -t file)
,步骤如下:
- 打开vim编辑器。
- 输入命令模式,按下冒号(:)键。
- 输入set list命令,按下回车键。
- 现在,你应该能够在文本中看到分割符的符号了。
-
cat -A|-t
:不确定可通过man cat
查看对应的参数
后话:如果还有新的点会开另外一篇进行梳理和总结,有兴趣的看官点个关注~后面会有一篇专门总结和讨论数据处理效率的笔记。
Ps:seniors工程师都看不下去眼了,但新手总归会犯点错,多多总结,积极交流。
参考及推荐
网友评论