3 votes

Python 3.x - Comment diviser efficacement un tableau d'objets en petits fichiers batch ?

Je suis assez novice en Python et j'essaie de diviser un fichier texte dont les entrées sont constituées de deux lignes en lots de max. 400 objets.

Les données avec lesquelles je travaille sont des milliers de séquences au format FASTA (texte brut avec un en-tête, utilisé en bio-informatique) dont les entrées ressemblent à ceci :

>HORVU6Hr1G000325.5

PIPPPASHFHPHHQNPSAATQPLCAAMAPAAKKPPLKSSSSHNSAAGDAA

>HORVU6Hr1G000326.1

MVKFTAEELRGIMDKKNNIRNMSVIAHVD

...

Dans Biopython, il existe un analyseur SeqIO.parse qui permet d'accéder à ces données sous la forme d'un tableau d'objets composé d'ID et de chaînes de caractères, que je dois utiliser dans des parties ultérieures de mon code, et comme je dois être économe en mémoire, j'aimerais éviter de lire et d'analyser le fichier source plus de fois que nécessaire.

Dans le manuel de Biopython, il est recommandé de faire cela via un générateur, ce que j'utilise : https://biopython.org/wiki/Split_large_file

Cependant, j'utilise Python 3.7 alors que le code est en Python 2.x, il y a donc des changements nécessaires. J'ai modifié le fichier

entry = iterator.next()

en

entry = next(iterator)

mais je ne sais pas si c'est tout ce que je dois changer.

Voici le code :

def batch_iterator(iterator, batch_size=400):
    """Returns lists of length batch_size."""
    entry = True  # Make sure we loop once
    while entry:
        batch = []
        while len(batch) < batch_size:
            try:
                entry = next(iterator)
            except StopIteration:
                entry = None

            if entry is None:
                # End of file
                break
            batch.append(entry)
        if batch:
            yield batch

while True:
    bsequence = input("Please enter the full path to your FASTA file(e.g. c:\\folder1\\folder2\\protein.fasta):\n")
    try:
        fastafile = open(bsequence)
        break
    except:
        print("File not found!\n")            

record_iter = SeqIO.parse(fastafile,"fasta")
num = 0
for line in fastafile:
    if line.startswith(">"):
        num += 1

print("num=%i" % (num,))
if num > 400:
    print("The specified file contains %i sequences. It's recommended to split the FASTA file into batches of max. 400 sequences.\n" % (num,))
    while True:
        decision = input("Do you wish to create batch files? (Original file will not be overwritten)\n(Y/N):")
        if (decision == 'Y' or 'y'):
            for i, batch in enumerate(batch_iterator(record_iter, 400), 1):
                filename = "group_%i.fasta" % (i + 1)
                with open(filename, "w") as handle:
                    count = SeqIO.write(batch, handle, "fasta")
                print("Wrote %i records to %s" % (count, filename))
            break
        elif (decision == 'N' or 'n'):
            break
        else:
            print('Invalid input\n')

...next part of the code

Lorsque j'exécute ce programme, après l'invite Y/N, même si je tape Y, le programme passe à la partie suivante de mon code sans créer de nouveau fichier. Le débogueur affiche ce qui suit :

Do you wish to create batch files? (Original file will not be overwritten)
(Y/N):Y
Traceback (most recent call last):
  File "\Biopython\mainscript.py", line 32, in batch_iterator
    entry = next(iterator)
StopIteration

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1569, in _trace
    return self._trace_and_catch(frame, event, arg)

  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1611, in _trace_and_catch
    frame.f_back, event, marker_function_args, node

  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1656, in _handle_progress_event
    self._save_current_state(frame, event, args, node)

  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1738, in _save_current_state
    exception_info = self._export_exception_info()

  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1371, in _export_exception_info
    "affected_frame_ids": exc[1]._affected_frame_ids_,

AttributeError: 'StopIteration' object has no attribute '_affected_frame_ids_'

Y a-t-il une différence entre Python 2.x et 3.x qui m'échappe ? Le problème est-il ailleurs ? Cette approche est-elle complètement erronée ? Merci d'avance !

3voto

jfaccioni Points 4793

Je ne peux pas vérifier l'ensemble de votre code puisque vous en avez omis une partie, mais je vois deux choses erronées ici :

num = 0
for line in fastafile:
    if line.startswith(">"):
        num += 1

Ces lignes épuisent l'objet de votre dossier fastafile . Supprimez entièrement ces lignes (et n'oubliez pas de corriger l'indentation ci-dessous, supprimez la ligne if num > 400: ).

if (decision == 'Y' or 'y'):

Cela ne fait pas ce que vous pensez. Remplacez-la par if decision in ('Y', 'y'): o if decision.lower() == 'y': . Vous répétez ce schéma ci-dessous dans la ligne if (decision == 'N' or 'n'): Il convient donc de le modifier également.

Effectuez les modifications et essayez d'exécuter à nouveau le code.

Explication

1er numéro En Python, un objet de type fichier (c'est-à-dire ce que l'on appelle un "fichier") est un objet de type "fichier". open('filename.txt', 'r') returns) est un générateur, ce qui signifie qu'il ne peut être itéré qu'une seule fois. Cela peut sembler un peu bizarre à première vue, mais c'est là tout l'intérêt de l'utilisation des générateurs. Un générateur en tant qu'objet fichier permet au fichier d'être parcouru en boucle ligne par ligne, sans jamais avoir à charger tout le contenu du fichier en une seule fois - le générateur se contente de garder la trace de la ligne suivante.

Le revers de la médaille est qu'ils ne peuvent pas revenir en arrière, donc lorsque vous écrivez votre for line in fastafile vous épuisez le générateur. Lorsque vous essayez ensuite d'appeler batch_iterator(record_iter, 400) , le générateur en record_iter est déjà épuisé, c'est pourquoi vous rencontrerez une erreur plus tard - le batch_iterator ne peut pas analyser les séquences fasta s'il n'y a plus rien à analyser.

2ème édition pour les conditionnelles avec des opérateurs booléens tels que if (decision == 'Y' or 'y'): Python évaluera toujours les deux parties individuellement. Ainsi, Python voit en fait if (bool(decision == 'Y') or bool('y')): .

Desde bool('y') s'évalue à True (comme toute chaîne non vide), votre expression devient if (bool(decision == 'Y') or True): ce qui est évidemment toujours vrai.

Utilisez l'une des méthodes proposées pour comparer une variable à plus d'une valeur dans une conditionnelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X