recherche

Maison  >  Questions et réponses  >  le corps du texte

python优化代码(文本查找)效率?

#!/usr/bin/env python
# -*- coding: utf-8 -*-
infile2 = open('genemark.gff3', 'r')
infile1 = set(line1.strip() for line1 in open('1.txt', 'r'))
for line in infile2:
    line = line.strip().split()
    if line[2] == 'gene':
        chr, start, end = line[0], int(line[3]), int(line[4])
        for line1 in infile1:
            line1 = line1.split()
            chr1, start1, end1 = line1[1], int(line1[2]), int(line1[3])
            if chr1 == chr:
                if start1 < start < end1:
                    print line1[0], line[-1]
                if start1 < end < end1:
                    print line1[0], line[-1]
                if start1 > start and end > end1:
                    print line1[0], line[-1]

genemark.gff3 格式类似下边:

chr1D    GeneMark.hmm    gene    2705930    2711118    .    +    .    ID=1903228_g;Name=1903228_g
chr1D    GeneMark.hmm    mRNA    2705930    2711118    .    +    .    ID=1903228_t;Name=1903228_t;Parent=1903228_g

1.txt:

UN011157 chr1D 2705329 2706342 98.4 95.0 972 30 21 0
UN003843 chr1D 2705681 2721144 61.4 97.4 633 12 5 0

附上原始文件的百度云链接,希望感兴趣的参考
点击下载 密码 enu8

综合楼下各位朋友的答案,现推荐两种
第一种 根据 @ferstar @用筹兮用严 的答案,即并行版

#!/usr/bin/env python
# encoding: utf-8

from collections import defaultdict
from multiprocessing import Pool, cpu_count
from functools import partial


def find_sth(f2, f1=None):
    start, end = int(f2[3]), int(f2[4])
    for uno1, start1, end1 in f1[f2[0]]:
        if (start1 <= start and start <= end1) or (start1 <= end and end <= end1) or (start1 >= start and end >= end1):
            with open("out.txt", "a") as fh:
                fh.write(uno1 + "\t" + f2[-1] + "\n")
                #print(uno1, f2[-1])


def main():
    with open('1.txt', 'r') as f1:
        infile1 = defaultdict(set)
        for uno1, chr1, start1, end1, *others in map(str.split, f1):
            infile1[chr1].add((uno1, int(start1), int(end1)))
    with open('genemark.gff3', 'r') as f2:
        infile2 = [x for x in map(str.split, f2) if x[2] == 'gene']
    pool = Pool(cpu_count())
    pool.map(partial(find_sth, f1=infile1), infile2)
    pool.close()
    pool.join()


if __name__ == "__main__":
    main()

第二种 @citaret 他的版本(单核版),对单核来说,不逊于上述代码。但是两者结果稍有不同,并行版结果更全(这里少了73条,出在判断条件的边界问题,由于对intervaltree熟悉,怎么改还不知道),现在边界问题已修改,两种代码结果完全一样,perfect!
如下


from collections import defaultdict
from intervaltree import Interval, IntervalTree

with open('1.txt') as f:
    d1 = defaultdict(list)
    xs = map(lambda x: x.strip().split(), f)
    for x in xs:
        y = (x[0], int(x[2]), int(x[3]))
        d1[x[1]].append(y)

    for k, v in d1.items():
        d1[k] = IntervalTree(Interval(s, e, u) for u, s, e in v)

with open('genemark.gff3') as f:
    for line in f:
        line = line.strip().split()
        if line[2] == 'gene':
            chr, start, end = line[0], int(line[3]), int(line[4])
            for start1, end1, un1 in d1[chr][start-1:end+1]:
                print(un1, line[-1])
PHP中文网PHP中文网2889 Il y a quelques jours460

répondre à tous(6)je répondrai

  • 巴扎黑

    巴扎黑2017-04-18 09:18:12

    Il ne devrait pas y avoir de place pour une meilleure optimisation des performances, mais le code peut être légèrement ajusté

    with open('1.txt', 'r') as f1, open('2.txt', 'r') as f2:
        lines1 = [_.strip().split() for _ in f1]
        for line2 in f2:
            line2 = line2.strip().split()
            if line2[2] != 'gene':
                continue
    
            chr2, start2, end2 = line2[0], int(line2[3]), int(line2[4])
            for line1 in lines1:
                chr1, start1, end1 = line1[1], int(line1[2]), int(line1[3])
                if chr1 == chr2 and (start1 < start2 < end1 or start1 < end2 < end1 or start1 > start2 and end2 > end1):
                    print line1[0], line2[-1]

    répondre
    0
  • 阿神

    阿神2017-04-18 09:18:12

    Voici deux suggestions :

    1. Le code est trop profondément imbriqué. Dans la fonction, vous pouvez réduire le niveau d'imbrication en revenant le plus tôt possible. De même, dans la boucle, vous pouvez utiliser continuer pour réduire le niveau d'imbrication.

    2. À propos des performances

    for line1 in infile1:
        line1 = line1.split()
    

    Il est très imprudent de diviser les lignes du fichier1 à chaque fois dans la boucle

    Voici le code que j'ai modifié

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    infile2 = open('genemark.gff3', 'r')
    infile1 = {}
    for line1 in open('1.txt', 'r'):
        line1 = line1.strip().split()
        id, chr1, start1, end1 = line1[0], line1[1], int(line1[2]), int(line1[3])
        if not infile1.has_key(chr1):
            infile1[chr1] = []
        infile1[chr1].append({"start": start1, "end": end1, "id": id})
    
    for line in infile2:
        line = line.strip().split()
    
        if line[2] != 'gene':
            continue
    
        chr, start, end = line[0], int(line[3]), int(line[4])
    
        if not infile1.has_key(chr):
            continue
    
        for i in infile1[chr]:
            if i['start'] < start < i['end']:
                print i['id'], line[-1]
            if i['start'] < end < i['end']:
                print i['id'], line[-1]
            if i['start'] > start and i['end'] > end:
                print i['id'], line[-1]
    

    répondre
    0
  • PHPz

    PHPz2017-04-18 09:18:12

    Échangez de l'espace contre du temps pour construire respectivement une liste de genemark.gff3 et un dictionnaire de 1.txt.

    from collections import defaultdict
    
    with open('genemark.gff3') as f:
        ls = f.readlines()
        xs = map(lambda x: x.strip().split(), ls)
        t2 = (x for x in xs if x[2] == 'gene')
    
    with open('1.txt') as f:
        d1 = defaultdict(list)
        ls = f.readlines()
        xs = map(lambda x: x.strip().split(), ls)
        for x in xs:
            d1[x[1]].append(x)
    
    for line in t2:
        chr, start, end = line[0], int(line[3]), int(line[4])
        if chr in d1:
            for line1 in d1[chr]:
                chr1, start1, end1 = line1[1], int(line1[2]), int(line1[3])
                if start1 < start < end1:
                    print line1[0], line[-1]
                if start1 < end < end1:
                    print line1[0], line[-1]
                if start1 > start and end > end1:
                    print line1[0], line[-1]

    Version modifiée v2, élimine int() dans la boucle interne et simplifie la sortie.

    from collections import defaultdict
    
    with open('1.txt') as f:
        d1 = defaultdict(list)
        xs = map(lambda x: x.strip().split(), f)
        for x in xs:
            y = (x[0], int(x[2]), int(x[3]))
            d1[x[1]].append(y)
    
    with open('genemark.gff3') as f:
        for line in f:
            line = line.strip().split()
            chr, start, end = line[0], int(line[3]), int(line[4])
            for un1, start1, end1 in d1[chr]:
                if start < end1 and end > start1:
                    print un1, line[-1]

    v3 : Après avoir soigneusement étudié le sens de la question, nous avons constaté que la boucle principale consiste à trouver tous les fragments de l'ensemble qui croisent un fragment. Nous pouvons d'abord jeter un œil à cet ensemble :

    chr1D 7359
    chr2D 9219
    chr2B 9486
    chr2A 8986
    chr6B 7178
    chr6A 6446
    chr6D 6093
    chr4A 7543
    chr4B 7086
    chr4D 6316
    ...
    

    Le nombre de fragments dans chaque collection est de 6 000 à 10 000 et le parcours est inefficace. Par conséquent, envisagez d'utiliser intervaltree pour obtenir rapidement tous les fragments qui croisent un fragment.
    from collections import defaultdict
    from intervaltree import Interval, IntervalTree
    
    with open('1.txt') as f:
        d1 = defaultdict(list)
        xs = map(lambda x: x.strip().split(), f)
        for x in xs:
            y = (x[0], int(x[2]), int(x[3]))
            d1[x[1]].append(y)
    
        for k, v in d1.items():
            d1[k] = IntervalTree(Interval(s, e, u) for u, s, e in v)
    
    with open('genemark.gff3') as f:
        for line in f:
            line = line.strip().split()
            if line[2] != 'gene': continue
            chr, start, end = line[0], int(line[3]), int(line[4])
            for start1, end1, un1 in d1[chr][start:end]:
                print un1, line[-1]

    Le résultat du test de temps est le suivant : il faut 10 secondes pour créer un arbre d'intervalle, mais la vitesse du processus d'intersection est améliorée d'environ 100 fois.

    référence d'intervalle https://pypi.python.org/pypi/...

    répondre
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-18 09:18:12

    Vous n'avez pas besoin de fermer le fichier après l'avoir ouvert

    répondre
    0
  • PHP中文网

    PHP中文网2017-04-18 09:18:12

    from collections import defaultdict
    
    with open('1.txt', 'r') as f1, open('genemark.gff3', 'r') as f2:
        infile1 = defaultdict(set)
        for uno1, chr1, start1, end1, *others in map(str.split, f1):
            infile1[chr1].add((uno1, int(start1), int(end1)))
        infile2 = filter(lambda x: x[2] == 'gene', map(str.split, f2))
        for chr, start, end, info in map(lambda x: (x[0], int(x[3]), int(x[4]), x[-1]), infile2):
            for uno1, start1, end1 in infile1[chr]:
                if start1 < start < end1 or start1 < end < end or (start1 > start and end > end1):
                    print(uno1, info)

    La parallélisation proposée par @ferstar au 6ème étage est le bon sens, mais il y a un problème avec le code...
    Je l'ai modifié :

    #!/usr/bin/env python
    # encoding: utf-8
    
    from collections import defaultdict
    from multiprocessing import Pool, cpu_count
    from functools import partial
    
    
    def find_sth(line, f1=None):
        line = line.split()
        if line[2] != 'gene':
            return
        start, end = int(line[3]), int(line[4])
        for uno1, start1, end1 in f1[line[0]]:
            if start1 < start < end1 or start1 < end < end or (start1 > start and end > end1):
                print(uno1, line[-1])
    
    
    def main():
        pool = Pool(cpu_count())
        with open('1.txt', 'r') as f1, open('genemark.gff3', 'r') as f2:
            infile1 = defaultdict(set)
            for uno1, chr1, start1, end1, *others in map(str.split, f1):
                infile1[chr1].add((uno1, int(start1), int(end1)))
            pool.map(partial(find_sth, f1=infile1), f2)
            #fw.writelines(filter(lambda x: x is not None, map(lambda x: x.get(), [pool.apply_async(func, (line,)) for line in f2])))
        pool.close()
        pool.join()
    
    
    if __name__ == "__main__":
        main()

    répondre
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-18 09:18:12

    J'ai trouvé une chose très intéressante. Tout le monde a répondu très positivement, mais quant aux résultats concrets, j'ai juste fait un petit test par ennui. Le processus est le suivant :

    Étant donné que l'exemple de texte fourni par la question ne comporte que deux lignes, j'ai doublé respectivement 1.txt et genemark.gff3 en 4000 lignes

    (qiime) [ngs@cluster ~]$ wc -l 1.txt 
    4000 1.txt
    (qiime) [ngs@cluster ~]$ wc -l genemark.gff3 
    4000 genemark.gff3

    Trier par nombre d'étages auxquels on a répondu. Par exemple, le code du questionneur est hi.py, puis le code du répondant au premier étage est hi1.py, et ainsi de suite

    .
    • Regardez d'abord le titre de la question

    (qiime) [ngs@cluster ~]$ time python hi.py > hi.txt
    real    0m0.049s
    user    0m0.042s
    sys    0m0.007s
    (qiime) [ngs@cluster ~]$ wc -l hi.txt 
    6000 hi.txt

    Ça semble répétitif

    • L'intimé au premier étage

    time python hi1.py > hi1.txt
    
    real    0m21.727s
    user    0m21.171s
    sys    0m0.547s
    (qiime) [ngs@cluster ~]$ wc -l hi1.txt 
    8000000 hi1.txt

    Je suis encore retourné chez grand-mère

    • L'intimé au deuxième étage

    (qiime) [ngs@cluster ~]$ time python hi2.py > hi2.txt
    
    real    0m16.326s
    user    0m14.550s
    sys    0m1.044s
    (qiime) [ngs@cluster ~]$ wc -l hi2.txt 
    12000000 hi2.txt
    • L'intimé au troisième étage

    (qiime) [ngs@cluster ~]$ time python hi3.py > hi3.txt
    
    real    0m27.079s
    user    0m26.281s
    sys    0m0.786s
    (qiime) [ngs@cluster ~]$ wc -l hi3.txt
    12000000 hi3.txt

    Le résultat du répondant au troisième étage est le même que celui du deuxième étage, mais il est plus de 10 secondes plus lent

    • Réponse du quatrième étage (j'ai fait du tort à mes camarades de classe du quatrième étage, c'est le code py3)

    (py3) [ngs@cluster ~]$ time python hi4.py > hi4.txt
    
    real    0m0.074s
    user    0m0.064s
    sys    0m0.010s
    (py3) [ngs@cluster ~]$ wc -l hi4.txt 
    4000 hi4.txt

    Effectivement, la communication mène au progrès, et le résultat actuel est correct

    Résumé

    En fait, il semble que les résultats du code du questionneur soient dupliqués, et le résultat du répondant du quatrième étage est correct

    Mon plan - apporter de petites modifications au code du quatrième étage en parallèle

    Il y avait quelque chose qui n'allait pas avec ce que j'ai écrit, @yongchixiyongyan a mis à jour le bon code parallèle, et mon code ne sera pas modifié, afin qu'il puisse être référencé par ses camarades de classe qui le verront plus tard

    Notation directe (python3)

    from collections import defaultdict
    import multiprocessing
    
    
    def find_sth(x):
        with open('1.txt', 'r') as f1:
            infile1 = defaultdict(set)
            for uno1, chr1, start1, end1, *others in map(str.split, f1):
                infile1[chr1].add((uno1, int(start1), int(end1)))
            chr, start, end, info = x[0], int(x[3]), int(x[4]), x[-1]
            for uno1, start1, end1 in infile1[chr]:
                if start1 < start < end1 or start1 < end < end or (start1 > start and end > end1):
                    print(uno1, info)
    
    
    def main():
        with open('genemark.gff3', 'r') as fh:
            lst = [x for x in map(str.split, fh) if x[2] == 'gene']
        pool = multiprocessing.Pool(multiprocessing.cpu_count())
        pool.map(find_sth, lst)
        pool.close()
        pool.join()
    
    if __name__ == "__main__":
        main()

    Ensuite, regardez l'efficacité opérationnelle

    (py3) [ngs@cluster ~]$ time python hi_new.py > hi_new.txt
    real    0m3.033s
    user    0m31.952s
    sys    0m0.219s
    
    (py3) [ngs@cluster ~]$ wc -l hi_new.txt 
    4000 hi_new.txt

    Cela semble être beaucoup plus lent en termes de temps (4000 lignes de données ne font que quelques centaines de Ko). Le questionneur peut essayer de le tester avec vos données réelles. Plus les données sont traitées, plus c'est évident. avantage d'efficacité du traitement parallèle

    PS : J'estime que la taille des données réellement traitées par le questionneur doit être au niveau du Mo voire du Go. Le traitement parallèle à ce niveau est la voie à suivre

    .

    Données source et lien vers l'adresse du résultat : http://pan.baidu.com/s/1hrSZQuS Mot de passe : u93n

    répondre
    0
  • Annulerrépondre