Chinaunix

标题: 求教字符串快速比较问题 [打印本页]

作者: hulnglei    时间: 2010-06-01 15:30
标题: 求教字符串快速比较问题
求教各位大虾 字符串比较问题:

随意两个长度相同的字符串:
”AATTCCCCCGCGATCGGTTTTT"
"AATTCCCCCGCGATCGGTTATT"
如果字符串只有一个字符不对就保留,其他的放弃
由于是一百多万个字符串和另外一百多万个个比较。我本来期望用a这样一个一个的比较字符,用count计数。后来发现算的太慢。加上其他的计算量得好几年。所以求教快速的算法!
作者: t6760915    时间: 2010-06-01 16:00
本帖最后由 t6760915 于 2010-06-01 16:03 编辑

你还发个代码的demo,其实慢在什么地方你也不知道呢,不如说你取这些自符串的地方,可能会慢,也可能是比较的地方。。。
作者: hulnglei    时间: 2010-06-01 16:23
本帖最后由 hulnglei 于 2010-06-01 16:24 编辑

回复 2# t6760915


    确实如此,我也只是估计!

两个输入文件都是这样格式:都是大概80M大小

@080404_HWI-EAS121_0001FC209NNAAXX_4_1_931_319
AATTCTAACATGAAAGTAGGAAAGATGTCAC
@080404_HWI-EAS121_0001FC209NNAAXX_4_1_906_759
AATTCACAGCATTGCAGTAGGTTTTTTATCT
@080404_HWI-EAS121_0001FC209NNAAXX_4_1_895_807
AATTCAACAACAGGACATTAATTGGAAACGG
@080404_HWI-EAS121_0001FC209NNAAXX_4_1_905_794
AATTCAGAGCGCCACCCCATGGTGCCTCCTG

上面是字符串名字,下面行就是字符串,我就是整个比较字符串

我的脚本:


#!/usr/bin/python
# Filename: read the fastq file from p1 and p1 , conpare the each two sequences# to find snp. I think if there is a snp, the nulcleotide among it is the same

import sys

class Fastq:
        def __init__(self, name, seq):
                self.name = name
                self.seq = seq

def get_fastq(fh):
        """ read a fastq entry from a file handle """

        header = fh.readline()
# eof detection
        if not header:
                return None
# no fastq format
        if header[0] != '@':
                return None

        seq = ""
        line = fh.readline()
        seq = line[:-1]
        line = fh.readline()
        while line:
                if line[0] == '@':
                        fh.seek(-len(line), 1)
                        break
                line = fh.readline()

        return Fastq(header[1:-1], seq)


fip1 = open("seqp1.fasta")
fo = file("outsnp.txt",'w')
entryp1 = get_fastq(fip1)
while entryp1:
     #... do what you have to do
        fip2 = open("seqp2.fasta")
        entryp2 = get_fastq(fip2)
        while entryp2:
                snplist = []
                count = 0
                for i in range(5, len(entryp1.seq)):
                        if entryp1.seq!=entryp2.seq:
                                count = count + 1
                                i= i+ 1
                if count<2:
                        for j in range(5, len(entryp1.seq)):
                                if entryp1.seq[j]!=entryp2.seq[j]:
                                        snplist.append(j)
                                        snplist.append(entryp1.seq[j])
                                        snplist.append(entryp2.seq[j])
                                j=j+1

                        print >> fo, entryp1.name, entryp2.name, snplist
                entryp2 = get_fastq(fip2)
        fip2.close()
        print entryp1.name
        entryp1 = get_fastq(fip1)

fip1.close()
fo.close()
作者: t6760915    时间: 2010-06-01 16:58
回复  t6760915


    确实如此,我也只是估计!

两个输入文件都是这样格式:都是大概80M大小

@08 ...
hulnglei 发表于 2010-06-01 16:23



    读文件你可以采用for line in open('a.txt', 'r'):的方法一行一行读,写结果文件我觉得你的行数不太多的暂时保存在list里面,程序结束后将

result = '\n'.join(resultList)写入文件,你现在的情况可能每次比较后都要写文件,那自然是慢好多了
作者: t6760915    时间: 2010-06-01 17:22
本帖最后由 t6760915 于 2010-06-01 17:24 编辑
  1. #coding=utf-8

  2. import os
  3. import sys

  4. class cmp:
  5.     adict = {}
  6.     bdict = {}

  7.     def __init__(self):
  8.         self.adict = self.loadFile('a.txt')
  9.         self.bdict = self.loadFile('b.txt')

  10.     def loadFile(self, path):
  11.         if not path:
  12.             return False
  13.         
  14.         if not os.path.exists(path):
  15.             return False
  16.         
  17.         try:
  18.             fp = open(path, 'r')
  19.             
  20.             lines = []
  21.             for line in fp:
  22.                 lines.append(line.strip())

  23.             fp.close()
  24.         except:
  25.             return False
  26.         
  27.         if not lines:
  28.             return False
  29.         
  30.         names = []
  31.         values = []
  32.         for linnnum,line in enumerate(lines):
  33.             if not line:
  34.                 continue
  35.             
  36.             if linnnum % 2 == 0:
  37.                 names.append(line)
  38.             else:
  39.                 values.append(line)
  40.         
  41.         return dict(zip(names, values))

  42.     def run(self):
  43.         for key,val in self.adict.items():
  44.             if not self.bdict.has_key(key):
  45.                 print 'key %s not exists in b.txt' % key
  46.             else:
  47.                 if self.bdict[key] == val:
  48.                     print 'key %s eqal in a.txt and b.txt' % key
  49.                 else:
  50.                     print 'key %s not eqal in a.txt and b.txt' % key

  51.     def __del__(self):
  52.         self.adict = {}
  53.         self.bdict = {}

  54. if __name__ == '__main__':
  55.     c = cmp()
  56.     c.run()
复制代码
cmp.rar (999 Bytes, 下载次数: 46)
  1. D:\备份\cmp>python cmp.py
  2. key @080404_HWI-EAS121_0001FC209NNAAXX_4_1_905_794 eqal in a.txt and b.txt
  3. key @080404_HWI-EAS121_0001FC209NNAAXX_4_3_905_794 not exists in b.txt
  4. key @080404_HWI-EAS121_0001FC209NNAAXX_4_1_931_319 not eqal in a.txt and b.tx
  5. key @080404_HWI-EAS121_0001FC209NNAAXX_4_1_895_807 not eqal in a.txt and b.tx
  6. key @080404_HWI-EAS121_0001FC209NNAAXX_4_1_906_759 eqal in a.txt and b.txt

  7. D:\备份\cmp>
复制代码

作者: hulnglei    时间: 2010-06-01 19:03
本帖最后由 hulnglei 于 2010-06-01 21:41 编辑

回复 4# t6760915


太感谢t6760915 不过有没有加速比较的函数啊?
真的很感谢你的脚本,不过我的重点还是在比较。两个文件其实是从一个文件里面分离的,每一个序列的名字都是不同的。序列也是要比较,只能有1个字符的不同。多少都不行! 我试了下,就算不写入,还是很慢!唉!

我试用了下我的文件,飞快!不过要是要比较的函数就更好了
万分感谢

作者: luffy.deng    时间: 2010-06-01 19:05
本帖最后由 luffy.deng 于 2010-06-01 19:16 编辑
while entryp1:
     #... do what you have to do
        fip2 = open("seqp2.fasta")
        entryp2 = get_fastq(fip2)
         while
        .....
        fip2.close()
        ......
hulnglei 发表于 2010-06-01 16:23

光凭这一条 你就很欠扁。
作者: hulnglei    时间: 2010-06-01 19:16
本帖最后由 hulnglei 于 2010-06-01 19:18 编辑

&ptid
光凭这一条 你就很欠扁。
luffy.deng 发表于 2010-06-01 19:05


? 不理解!我本来就是新手,如果被扁后能学到东西。我也是很愿意被扁的。 所以麻烦你解释清楚下好吗?
作者: luffy.deng    时间: 2010-06-01 19:38
seqp2.fasta从来没有改变过,你却把它打开关闭 100多万次,另外一行行的读  效率不会太高。80多兆完全可以考虑一次读取。其次字符串是否是定长?比较规则是什么 就是s1[1] == s2[1]    s1[2] == s2[2]  s1[3] == s2[3] ......这样?
作者: hulnglei    时间: 2010-06-01 19:50
seqp2.fasta从来没有改变过,你却把它打开关闭 100多万次,另外一行行的读  效率不会太高。80多兆完全可以考 ...
luffy.deng 发表于 2010-06-01 19:38


太感谢了。

不同字符串比较就是生物里面的不同的DNA序列比较。我本来搞生物的,现在赶鸭子上架才学。所以只知道一行一行读,正在吃力的看5楼的代码

字符串是定长的31。我就是想请教下有什么好的比较规则或者函数没有。反正最终目标是两个字符串之间只有一个字符是不同的,其他都是一一对应。实在不行,其他判断字符串很相似的标准也行。
作者: HyryStudio    时间: 2010-06-01 19:56
本帖最后由 HyryStudio 于 2010-06-01 20:02 编辑

回复 1# hulnglei

这种运算应该用C语言写一个扩展然后用Python调用。

如果非要用Python写,可以如下优化:

如果大多数比较都不成功,即大多数都相差不止一个或者相同的话,可以先比较,只有成功之后再找出那个不同的元素:

下面的compare函数当找到两个不同时立即返回,可以提高比较速度。
  1. from itertools import izip
  2. def compare(a, b):
  3.     count = 0
  4.     for x,y in izip(a,b):
  5.         if x!=y:
  6.             count += 1
  7.             if count > 1: return False
  8.     if count == 1: return True
  9.     return False
复制代码
另外可考虑用Psyco加速。

或者同时找到不同的元素,因为只有一个不同,因此不需要用列表保存:
  1. def compare(a, b):
  2.     count = 0
  3.    
  4.     for x,y in izip(a,b):
  5.         if x!=y:
  6.             dif1 = x
  7.             dif2 = y
  8.             if count == 1: return None
  9.             count += 1
  10.     if count == 1: return dif1,dif2
  11.     return None
复制代码

作者: luffy.deng    时间: 2010-06-01 21:51
本帖最后由 luffy.deng 于 2010-06-02 06:24 编辑
回复  t6760915


我的重点还是在比较。两个文件其实是从一个文件里面分离的,每一个序列的名字都是不同的。序列也是要比较,只能有1个字符的不同 ...
hulnglei 发表于 2010-06-01 19:03

两个文件如果需要按字符串名称对应的话,生成两个字典就可以了,
逐个比较很慢么?比如这样
  1. def cmp(ss,ds):
  2.     n= 31
  3.     res=0
  4.     while n>0 :
  5.         if ss[n-1:n] != ds[n-1:n]:
  6.             res+=1
  7.         if res>1:
  8.             return False            
  9.         n-=1
  10.      if res == 0:
  11.         return False
  12.      return True
复制代码

作者: luffy.deng    时间: 2010-06-01 22:03
本帖最后由 luffy.deng 于 2010-06-02 07:32 编辑

我用两个完全一样的字符串测试了一下(这应该是最差的情况),循环一百万次 需要3分钟。你为何非得纠结于如何字符串如何比较?瓶颈根本就不在这里。

  1. s1 ='AATTCTAACATGAAAGTAGGAAAGATGTCAC'
  2. s2 ='AATTCTAACATGAAAGTAGGAAAGATGTCAC'
  3. def cmp(ss,ds):
  4.     n= 31
  5.     res=0
  6.     while n>0 :
  7.         if ss[n-1:n] != ds[n-1:n]:
  8.             res+=1
  9.         if res>1:
  10.             return False            
  11.         n-=1
  12.     if res ==0:
  13.         return False
  14.     return True

  15. count =1000000
  16. while count:
  17.     print cmp(s1,s2)
  18.     count-=1
复制代码

作者: hulnglei    时间: 2010-06-02 10:33
本帖最后由 hulnglei 于 2010-06-02 10:44 编辑

回复 11# HyryStudio


   对对对!多谢!没想到可以先比。至于C语言加速我不会啊!呵呵!我昨晚试了下5楼大虾的代码。11个小时就可以算1/4了 本来已经很惊讶,不过我的数据只有极少数的字符串的相同的。所以。。。。。
作者: hulnglei    时间: 2010-06-02 10:39
回复 13# luffy.deng


  多谢luffy.deng 这就可以更加加速了。呵呵!主要我不知道该在哪方面修改脚本?而且计较次数比较多,两个文件都有1,800,000个项。这样比较的次数就太多了。以后可能还要修改下计算更多!
作者: luffy.deng    时间: 2010-06-02 10:54
回复 15# hulnglei
即使是180万次  我在一台奔4单核3GHz上也不过跑了6分20秒。所以字符串比较不是瓶颈 ,逐个比较完全就可以。
作者: hulnglei    时间: 2010-06-02 15:14
回复  hulnglei
即使是180万次  我在一台奔4单核3GHz上也不过跑了6分20秒。所以字符串比较不是瓶颈 ,逐个 ...
luffy.deng 发表于 2010-06-02 10:54



   我的意思的1,800,000*1,800,000次比较。 不过有你的那种方法就不存在这个问题了。现在运算速度已经很满意了。谢谢你和上面的各位大虾!特别是t6760915 那么长的代码。学到了不少。
作者: t6760915    时间: 2010-06-02 16:11
哈,不客气
作者: HyryStudio    时间: 2010-06-02 20:52
回复 10# hulnglei
如果你希望在100万个和100万个之间任何一对进行比较的话,需要考虑一下算法。例如可以将其中的一组建立一棵树,字符串的每个字符是一个节点,它的子节点是下一个字符,因此树的深度是字符串的长度。然后在此树中对另外一组的每个字符串进行搜索,当发现有两个节点不同时就可以停止搜索。这时可以省略后续所有节点的搜索,应该可以成数量级的加快计算速度。

如果你的字符串中的字符只有ATCG四个字符的话,并且长度是31的话,可以考虑使用两个32bit的长整型数保存其状态。这样可以通过异或位操作快速比较,当然还需要一个数比特1个数的函数,这些在C语言级别做的话应该是非常快的。
作者: Kabie    时间: 2010-06-02 21:27
呃……要n^2的比较啊。。。怪不得

这个可能还是写个hash好点。。。

另外我看你那个数据怎么有个是30的。。。。
作者: luffy.deng    时间: 2010-06-02 22:18
本帖最后由 luffy.deng 于 2010-06-03 10:43 编辑

回复 19# HyryStudio
回复  hulnglei
如果你希望在100万个和100万个之间任何一对进行比较的话,需要考虑一下算法。例如可以将其中的一组建立一棵树,字符串的每个字符是一个节点,它的子节点是下一个字符,因此树的深度是字符串的长度。然后在此树中对另外一组的每个字符串进行搜索,当发现有两个节点不同时就可以停止搜索。这时可以省略后续所有节点的搜索,应该可以成数量级的加快计算速度。

如果你的字符串中的字符只有ATCG四个字符的话,并且长度是31的话,可以考虑使用两个32bit的长整型数保存其状态。这样可以通过异或位操作快速比较,当然还需要一个数比特1个数的函数,这些在C语言级别做的话应该是非常快的。
HyryStudio 发表于 2010-06-02 20:52


这个很牛,不妨实现出来看一下?
关于字符串比较( 比如 c的strcmp)大体的实现都是这样
  1.   
  2. while (*s1 != 0 && *s1 == *s2)
  3.     s1++, s2++;

  4.   if (*s1 == 0 || *s2 == 0)
  5.     return (unsigned char) *s1 - (unsigned char) *s2;
  6.   return *s1 - *s2;
  7.   
复制代码
内核里的实现原理跟上面一样,只是用了内联汇编
  1. extern inline int strcmp(const char * cs,const char * ct)
  2. {
  3. register int __res __asm__("ax");
  4. __asm__("cld\n"
  5.         "1:\tlodsb\n\t"
  6.         "scasb\n\t"
  7.         "jne 2f\n\t"
  8.         "testb %%al,%%al\n\t"
  9.         "jne 1b\n\t"
  10.         "xorl %%eax,%%eax\n\t"
  11.         "jmp 3f\n"
  12.         "2:\tmovl $1,%%eax\n\t"
  13.         "jb 3f\n\t"
  14.         "negl %%eax\n"
  15.         "3:"
  16.         :"=a" (__res):"D" (cs),"S" (ct):"si","di");
  17. return __res;
  18. }
复制代码

作者: hulnglei    时间: 2010-06-03 16:41
回复  hulnglei
如果你希望在100万个和100万个之间任何一对进行比较的话,需要考虑一下算法。例如可以将其 ...
HyryStudio 发表于 2010-06-02 20:52



   谢谢,我当时听C语言数据结构的没好好听。C语言忘的一干二净。这树怎么做我真还得好好学学。现在项目赶,暂时是没工夫了。
作者: hulnglei    时间: 2010-06-03 16:48
回复  HyryStudio


这个很牛,不妨实现出来看一下?
关于字符串比较( 比如 c的strcmp)大体的实现都是这 ...
luffy.deng 发表于 2010-06-02 22:18


呵呵!后来发现自己还是搞错了。不用树来算的话。还是很慢。算还是得一个多月。这肯定是不行的了。我只能找找有没有用树算的软件。树的话只能请教我朋友写下了。谢谢各位!
作者: HyryStudio    时间: 2010-06-03 19:41
回复 23# hulnglei

用树写其实也不难,不过最好还是用C语言写,才能够达到飞快,不过这个比较费时间了。我想了一个很好的办法,你看是否可行:

假设A,B两个组,每个180万个长度为31的每位有ATCG等四种可能的字符串。
把B组做成一个集合set,或者字典,关键字是字符串,值是它们的名字。

对于A组中的每一个字符串,在B中找是否有和它相差一个元素的。
由于字典只能找完全一样的,因此具体这样做:

因为你只找相差一个字符的,因此对于字符串中的每个字符,都修改为其它三个,这样31*3 = 93,也就是每个字符串搜索93次。也就是穷举和此字符串相差一个的所有可能情况,然后都到字典中去找:
例如对于

AC....
搜索
TC...., CC..., GC..., AA..., AT..., AG...,


这样搜索空间从180万*180万降低到180万*93,应该能在有限的时间之内完成。

另外,先用2个32位的整数对数据进行编码再做成字典的话,能够节省不少内存。不过这一步不做也可,只要你内存足够大。
作者: Kabie    时间: 2010-06-04 11:33
楼上这解法很棒啊。。。
作者: hulnglei    时间: 2010-06-08 13:38
回复  hulnglei

用树写其实也不难,不过最好还是用C语言写,才能够达到飞快,不过这个比较费时间了。我 ...
HyryStudio 发表于 2010-06-03 19:41



   我试完。几分钟就算完了!太爽了。终于真的可以拿来用了!太感谢了!
作者: luffy.deng    时间: 2010-06-08 15:17
本帖最后由 luffy.deng 于 2010-06-08 15:22 编辑
回复  hulnglei

因为你只找相差一个字符的,因此对于字符串中的每个字符,都修改为其它三个,这样31*3 = 93,也就是每个字符串搜索93次。也就是穷举和此字符串相差一个的所有可能情况,然后都到字典中去找:
例如对于

AC....
搜索
TC...., CC..., GC..., AA..., AT..., AG...,


这样搜索空间从180万*180万降低到180万*93,应该能在有限的时间之内完成。 ...
HyryStudio 发表于 2010-06-03 19:41

这个办法很很牛X,我简单试了一下在我的机器上只跑了14分钟多。
  1. s ='AATTCTAACATGAAAGTAGGAAAGATGTCAC'
  2. d={'AATTCTAACATGAAAGTAGGAAAGATGTCAC':0}
  3. def genstr(s,d):
  4.     n=31
  5.     while n>0:
  6.         for i in 'ATCG':
  7.             if i !=s[n-1:n]:
  8.                 d.has_key(s[0:n-1]+i+s[n:])
  9.         n-=1
  10. t=1800000
  11. while t>0:
  12.     print t
  13.     genstr(s,d)
  14.     t-=1
复制代码

作者: GOD_Father    时间: 2010-06-08 20:42

思想无极限。。受益良深
作者: HyryStudio    时间: 2010-06-08 20:54
回复 24# HyryStudio

用Python写了一个用搜索树实现的,结果发现当树很平衡很大时,每个字符串的搜索量基本上和93差不了多少。穷举法对于Python来说还是最快的,因为dict是C语言级别实现的。
作者: hulnglei    时间: 2010-06-09 10:04
本帖最后由 hulnglei 于 2010-06-09 10:33 编辑
回复  hulnglei

用树写其实也不难,不过最好还是用C语言写,才能够达到飞快,不过这个比较费时间了。我 ...
HyryStudio 发表于 2010-06-03 19:41



   这样我顺便再问下,有兴趣的高手可以帮忙想下。如果两个字符串不是只有一个字符不同。有两个,三个,或者四个呢? 在生物上面单个字符可以解释为SNP, 2-4个也是可以解释为单倍型。也是有很用。
  1.                 fo = file("out_3snp.txt",'w')
  2.                 for keya,vala in self.adict.items():
  3.                         if vala in self.bdict:
  4.                                 continue
  5.                         else:
  6.                                 for i1 in range(5, 31):
  7.                                         for j1 in 'ACGT':
  8.                                                 if vala[i1] == j1:
  9.                                                         continue
  10.                                                 else:
  11.                                                         b1= vala[0:i1]+j1+vala[i1+1:31]

  12.                                                 for i2 in range(5,31):
  13.                                                         if i2==i1:
  14.                                                                 if (b1 not in self.cdict) and (b1 in self.bdict):
  15.                                                                         print >> fo, keya, self.bdict[b1][0], vala[:31], i1+1, vala[i1+32]
  16.                                                                 break
  17.                                                         else:
  18.                                                                 for j2 in 'ATCG':
  19.                                                                         if vala[i2] == j2:
  20.                                                                                 continue
  21.                                                                         else:
  22.                                                                                 b2 = b1[0:i2]+j2+b1[i2+1:31]

  23.                                                                         for i3 in range(5, 31):
  24.                                                                                 if i3==i1 or i3==i2:
  25.                                                                                         if (b2 not in self.cdict) and (b2 in self.bdict):
  26.                                                                                                 print >> fo, keya, self.bdict[b2][0], vala[:31], i1+1, vala[i1], j1, vala[i1+32],
  27. i2+1, vala[i2], j2, vala[i2+32]
  28.                                                                                         break
  29.                                                                                 else:
  30.                                                                                         for j3 in 'ACGT':
  31.                                                                                                 if vala[i3] == j3:
  32.                                                                                                         continue
  33.                                                                                                 else:
  34.                                                                                                         b3 = b2[0:i3]+j3+b2[i3+1:31]

  35.                                                                                                 if (b3 not in self.cdict) and (b3 in self.bdict):
  36.                                                                                                         print >> fo, keya, self.bdict[b3][0], vala[:31], i1+1, vala[i1], j1, vala[i1+32], i2+1, vala[i2], j2, vala[i2+32], i3+1, vala[i3], j3, vala[i3+32]
  37. -- INSERT --                                                                              
复制代码





欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2