2020-008 Excel与基因名的故事(续)

Excel与基因名的故事(续)

8号的时候,刷知乎遇到一个问题。如何评价科学家重命名了多个人类基因,以避免被 Excel 自动纠正?没有别的方法吗?

虽然不知道知乎从何种途径观察到我在研究这个问题然后推了相关问题给我。但是这个问题确实推荐的不错。

从问题以及相关搜索中可以知道,7号,HGNC,也就是人类基因组组织基因命名委员会,发了一篇NG的Comment,介绍了他们新的基因命名指南。

Guidelines for human gene nomenclature 一文中提到:

Symbols that affect data handling and retrieval.

For example, all symbols that autoconverted to dates in Microsoft Excel have been changed (for example, SEPT1 is now SEPTIN1; MARCH1 is now MARCHF1); tRNA synthetase symbols that were also common words have been changed (for example, WARS is now WARS1; CARS is now CARS1).

也就是说,HGNC决定改名了!

啊,真是喜大普奔。虽然微软在该事中被诟病,但是,生物学家还是屈服了。怪不得我之前搜索的时候显示Sept2的官方名为SEPTIN2,原来他已经改完名了。

那为什么之前转换成功的是Sept2呢,一方面是数据库没有及时更新,还有就是,之前是鼠的基因,而HGNC管理的主要是人的基因,人的基因名中除了orf之外的字母都是全大写的。

本篇文章均为人的基因名。

续集

那为什么还有续集呢?

因为,我想测试一下代码的兼容性,替换sep和mar够不够,还需不需要替换其他的月份。那么,我就需要一个被Excel影响的基因名清单。

找遍全网居然没有?

哦不对,有一个。在公司微信群里,有人提到了一个R包HGNChelper。它的核心功能就是将不合法的基因名转变为合法的基因名(也就是HGNC官方认可的)。

稍微介绍一下:

library(HGNChelper)
human = c("FN1", "tp53", "UNKNOWNGENE","7-Sep", "9/7", "1-Mar", "Oct4", "4-Oct",
      "OCT4-PG4", "C19ORF71", "C19orf71")
checkGeneSymbols(human)

out:

Human gene symbols should be all upper-case except for the 'orf' in open reading frames. The case of some letters was corrected.x contains non-approved gene symbols             x Approved Suggested.Symbol
1          FN1     TRUE              FN1
2         tp53    FALSE             TP53
3  UNKNOWNGENE    FALSE             <NA>
4        7-Sep    FALSE            SEPT7
5          9/7    FALSE            SEPT7
6        1-Mar    FALSE MARC1 /// MARCH1
7         Oct4    FALSE           POU5F1
8        4-Oct    FALSE           POU5F1
9     OCT4-PG4    FALSE         POU5F1P4
10    C19ORF71    FALSE         C19orf71
11    C19orf71     TRUE         C19orf71

本例来源于:拯救那些被EXCEL篡改的基因名

看起来很厉害。但是,考虑到python中Pandas读取错误的基因名读进来后是日期类型,而R读进来是数字类型。虽然也不排除其他方式可能能够读取Excel中原始的字符2-Sep,但是对我而言,这个包并没有什么用。我暂时也不知道他们开发这个包是用于处理何种来源的原始数据。

不过,这个包的转换方式采用的是存储数据直接映射,那么,是不是就可以从中提取可能被影响的基因列表呢。

从他的GitHub下载了rda文件。

hgnc = new.env()
load('hgnc.table.rda',envir = hgnc)
print(unique(hgnc$hgnc.table$Approved.Symbol)[1:10])  # 数据太多,取前10

out:

 [1] "NFYC-AS1"   "IFITM2"     "IFITM3"     "PRDX6"      "SEC24B-AS1"
 [6] "ALDH1L1"    "KNOP1"      "CYB561D2"   "KIR2DL4"    "ARHGAP9"   

NFYC-AS1也会被影响的吗?

Alias symbols是0808y08y,但是这个在Excel中不会被改变啊。不是很懂。

放弃此路!

自己动手,丰衣足食

找不到清单,那我就搞一个清单。

基因名,就从最官方的HGNC爬。

本来打算直接上爬虫的,但是仔细一看,HGNC贴心的提供了HGNC REST web-service

那就容易了啊!

使用到的主要是两个接口。

示例 类型 功能
http://rest.genenames.org/fetch/symbol/ZNF3 fetch 返回基因名对应的信息
http://rest.genenames.org/search/symbol/ZNF3 search 搜索基因名,返回基因列表

第二个接口接受类UNIX的匹配符,如ZNF?ZNF*,接口不区分大小写。

所有易导致错误的基因名都应当是以月份的简称开头的,所以,搜索sep2*就可以获得sep开头的基因列表,然后拿到每个基因的Previous symbols和Alias symbols,就可以获得基因所有的名字了。

首先编写一个兼容两个接口的查询函数。

import requests
from urllib.parse import urljoin     # 用于拼接url
from collections import namedtuple
request_result = namedtuple('request_result',['request_type', 'content'])  # 命名元组可以通过属性访问值

class RequestTypeError(Exception):   # 自定义异常
    pass

def request_gene(request_content,request_type = 'search',request_content_type = 'symbol'):
    if request_type.lower().startswith('f'):
        request_type = 'fetch'
    elif request_type.lower().startswith('s'):
        request_type = 'search'
    else:
        raise RequestTypeError  # 当request_type得小写不是以f或s开头时就抛出异常
    headers = {'Accept': 'application/json'}
    domin = 'http://rest.genenames.org/'

    try:
        url = urljoin(domin,"/".join([request_type,request_content_type,request_content]))
        res = requests.get(url,headers = headers)
        content = res.json()
    except Exception:    # 兼容可能出现的所有错误
        return None
    
    num_found = content['response']['numFound'] #该内容给出结果的数量
    if num_found:
        return request_result(request_type,content['response']['docs']) #docs内存储了具体的内容,为列表
    else:
        return None

然后,写循环执行操作。

def main():
    months =  ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
    results = []
    for month in months:
        month_result = request_gene(month+'*',request_type='search') #使用*匹配月份名开头的基因名
        if month_result is None:
            continue
        for i in month_result.content:  #content有多个结果
            gene_symbol = i['symbol']
            gene_result = request_gene(gene_symbol,request_type='fetch')
            if gene_result is None:
                continue
            gene_result = gene_result.content[0]  # fetch只有一个结果
            if 'alias_symbol' in gene_result:
                results.extend([gene_symbol,'alias_symbol',alias_symbol] for alias_symbol in gene_result['alias_symbol'])
            if 'prev_symbol' in gene_result:
                 results.extend([gene_symbol,'prev_symbol',prev_symbol] for prev_symbol in gene_result['prev_symbol'])
    return results

获得结果,使用Pandas生成Excel。

results = main()
import pandas as pd
genes = pd.DataFrame(results,columns=['gene_symbol','other_symbol_type','other_symbol'])
genes.to_excel('genes.xlsx',index=False)

打开Excel。

???

说好的自动转换呢。看来Pandas存储的Excel在打开时还是能保存原样的。不过双击单元格再Enter就变过去了,这Excel有点意思。

那怎么办呢,生成csv再打开应该就可以了。

genes.to_csv('genes.csv',index=False)

打开。

果然,可以了。然后另存为genes_after_open.xlsx

after_open= pd.read_excel('genes_after_open.xlsx')
after_open.head()

整合,然后筛选出所有被改变的基因名。

from datetime import datetime
genes['gene_symbol_after_open'] = after_open['gene_symbol']
genes['other_symbol_after_open'] = after_open['other_symbol']
genes[genes.gene_symbol_after_open.apply(lambda x:isinstance(x,datetime))]

官方名字没有被错误更改的(看来改的没有遗漏)。

再看下other_symbol

genes[genes.other_symbol_after_open.apply(lambda x:isinstance(x,datetime))]

out:

看了下都是SEP基因和MARCH开头的基因。咦,不是说还有其他月份的吗?而且,不是说有一个MARC1和MARCH1被共同转换成1-Mar了吗?

查了下,原来MARC1基因的更改后名字居然是MTR开头的。

那,看来只进行基因名的匹配搜索已经不够了啊。

看了下接口,官方也提供了全文搜索的选项,接口类似http://rest.genenames.org/search/ZNF3

改一下request_gene函数和main中的调用方式。

import requests
from urllib.parse import urljoin
from collections import namedtuple
request_result = namedtuple('request_result',['request_type', 'content'])
class RequestTypeError(Exception):
    pass
def request_gene(request_content,request_type = 'search',request_content_type = None):
    if request_type.startswith('f'):
        request_type = 'fetch'
    elif request_type.startswith('s'):
        request_type = 'search'
    else:
        raise RequestTypeError
    headers = {'Accept': 'application/json'}
    domin = 'http://rest.genenames.org/'

    try:
        if request_content_type is None:
            url = urljoin(domin,"/".join([request_type,request_content]))
        else:
            url = urljoin(domin,"/".join([request_type,request_content_type,request_content]))
        res = requests.get(url,headers = headers)
        content = res.json()
    except Exception:
        return None
    num_found = content['response']['numFound']
    if num_found:
        return request_result(request_type,content['response']['docs'])
    else:
        return None
def main():
    months =  ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
    results = []
    for month in months:
        month_result = request_gene(month+'*',request_type='search')
        if month_result is None:
            continue
        for i in month_result.content:
            gene_symbol = i['symbol']
            gene_result = request_gene(gene_symbol,request_type='fetch',request_content_type='symbol')
            if gene_result is None:
                continue
            gene_result = gene_result.content[0]
            if 'alias_symbol' in gene_result:
                results.extend([gene_symbol,'alias_symbol',alias_symbol] for alias_symbol in gene_result['alias_symbol'])
            if 'prev_symbol' in gene_result:
                 results.extend([gene_symbol,'prev_symbol',prev_symbol] for prev_symbol in gene_result['prev_symbol'])
    return results
results = main()
genes = pd.DataFrame(results,columns=['gene_symbol','other_symbol_type','other_symbol'])
genes.to_csv('genes.csv',index=False)

重新跑,重复之前的操作保存生成Excel。

after_open= pd.read_excel('genes_after_open.xlsx')
genes['gene_symbol_after_open'] = after_open['gene_symbol']
genes['other_symbol_after_open'] = after_open['other_symbol']
from datetime import datetime
pd.set_option('display.max_rows', None) #显示所有行
genes[genes.other_symbol_after_open.apply(lambda x:isinstance(x,datetime))]

看了下,确实包含了之前没找到的MTAR1基因。

统计一下都有哪些开头。

import re
incorrect_symbols = genes[genes.other_symbol_after_open.apply(lambda x:isinstance(x,datetime))].other_symbol
incorrect_symbols.apply(lambda x:re.match(r'([A-Za-z]*)\d*',x).group(1)).value_counts()

out:

SEPT     16
MARCH    11
OCT       9
Mar       9
SEP       6
APR       4
DEC       3
MARC      2
FEB       2
Oct       1
NOV       1

看来确实只有sep和mar需要变换。Mar开头的就离谱。看一下。

genes[genes.other_symbol_after_open.apply(lambda x:isinstance(x,datetime))][incorrect_symbols.apply(lambda x:x.lower().startswith('mar')).values]

out:

有一些基因的alias_symbol是Mar开头的。现在通用的基因名基本都是大写,这些应该不会再使用了吧,不管他们。MARC和MARCH的问题确实解决不了。

最后转换一下,添加一个大写。

genes['other_symbol_after_transform'] = genes.other_symbol_after_open.apply(lambda x: datetime.strftime(x,'%b%#d').replace('Sep','SEPTIN').replace('Mar','MARCHF').upper() if isinstance(x,datetime) else x)

看一下prev_symbol的复原情况。

genes[(genes.other_symbol_after_open.apply(lambda x:isinstance(x,datetime))) & (genes.other_symbol_type == 'prev_symbol')]

out:

除了可恶的MARC之外都可以。这个表共31行,应该包含了所有27个刚被改名的基因。

再看一下alias_symbol

genes[(genes.other_symbol_after_open.apply(lambda x:isinstance(x,datetime))) & (genes.other_symbol_type == 'alias_symbol')]

out:

这个转换就比较不行了,主要是因为各种牛鬼蛇神都有,SEPTIN6、SEPTIN8居然还有SEP2的alias_symbol,只能在一些symbol上试图性的恢复一下。看来有一些基因的alias_symbol也不可靠。

总结

本文主要是通过找到所有被影响的基因名来验证之前的替换是否足够完备。从结果来看,之前的替换已经基本够用,就是需要在更换物种的时候进行相应的大小写切换和替换更改。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容