Mik*_*cre 4 c python cython python-3.x
我有一个相对简单的问题:给定基因组中的位置,返回该点的基因名称。
我现在解决这个问题的方法是在 cython: 中使用以下类:
class BedFile():
""" A Lookup Object """
def __init__(self, bedfile):
self.dict = {}
cdef int start, end
with open(bedfile) as infile:
for line in infile:
f = line.rstrip().split('\t')
if len(f) < 4:
continue
chr = f[0]
start = int(f[1])
end = int(f[2])
gene = f[3]
if chr not in self.dict:
self.dict[chr] = {}
self.dict[chr][gene] = (start, end)
def lookup(self, chromosome, location):
""" Lookup your gene. Returns the gene name """
cdef int l
l = int(location)
answer = ''
for k, v in self.dict[chromosome].items():
if v[0] < l < v[1]:
answer = k
break
if answer:
return answer
else:
return None
Run Code Online (Sandbox Code Playgroud)
完整的项目在这里: https: //github.com/MikeDacre/python_bed_lookup,尽管整个相关类都在上面。
代码的问题在于,生成的类/字典占用了人类基因组的大量内存,其中包含 1.1 亿个基因(这是一个 1.1 亿行长的文本文件)。大约两分钟后,当它达到 16GB 内存时,我在构建字典的过程中杀死了init函数。任何使用那么多内存的东西基本上都是无用的。
我确信我必须有一种更有效的方法来做到这一点,但我不是 C 程序员,而且我对 cython 很陌生。我的猜测是,我可以构建某种纯 C 结构来保存基因名称以及起始值和终止值。然后我可以将 Lookup() 转换为一个调用另一个名为 _lookup() 的 cdef 函数的函数,并使用该 cdef 函数执行实际查询。
在理想情况下,整个结构可以存在于内存中,并且大约 2,000,000 个条目(每个条目包含两个整数和一个字符串)占用不到 2GB 的内存。
编辑:我想出了如何使用 sqlite 对于大文件有效地执行此操作,要查看 sqlite 的完整代码,请参阅此处:https: //github.com/MikeDacre/python_bed_lookup
但是,我仍然认为上面的类可以使用 cython 进行优化,以使字典在内存中更小并且查找速度更快,任何建议都值得赞赏。
谢谢!
为了稍微扩展一下我在评论中的建议,不要将其存储(start,end)为元组,而是将其存储为简单的 Cython 定义的类:
cdef class StartEnd:
cdef public int start, end
def __init__(self, start, end):
self.start = start
self.end = end
Run Code Online (Sandbox Code Playgroud)
(您还可以更改整数类型以节省更多大小)。我不建议放弃 Python 字典,因为它们很容易使用,并且(我相信)经过优化,对于字符串键(Python 中常见)的情况相当有效。
我们可以使用 来粗略估计节省的大小sys.getsizeof。(请注意,这对于内置类和 Cython 类来说效果很好,但对于 Python 类来说效果不太好,所以不要太相信它。另请注意,结果取决于平台,因此您的结果可能会略有不同)。
>>> sys.getsizeof((1,2)) # tuple
64
>>> sys.getsizeof(1) # Python int
28
Run Code Online (Sandbox Code Playgroud)
(因此每个元组包含64+28+28=120字节)
>>> sys.getsizeof(StartEnd(1,2)) # my custom class
24
Run Code Online (Sandbox Code Playgroud)
(24 有意义:它是PyObject_Head(16 个字节:一个 64 位整数和一个指针)+ 2 个 32 位整数)。
因此,小了5倍,我认为这是一个好的开始。