Script de conversión FASTA-TO-TSV -- python campo con parsing campo con reinventing-the-wheel campo con bioinformatics camp codereview Relacionados El problema

FASTA-to-tsv conversion script


11
vote

problema

Español

Funcioné en un script que toma como ingresa un archivo 998877665554433111 el número de cada aminoácido / secuencia en un formato 99887776655443312 .

Así es como un archivo de 99887776655443313

  Flexbox4  

Sé que hay bibliotecas que hacen lo mismo, sin embargo, tuve que escribir este script como tarea, sin la ayuda de Flexbox5 u otras bibliotecas similares.

Mi pregunta es: ¿Cómo puedo hacer que este código sea más eficiente?

  Flexbox6  
Original en ingles

I worked on a script that takes as input a multi-sequence .fasta file and outputs the number of each aminoacid/sequence in a .tsv format.

This is how a .fasta file looks:

>sp|P21515|ACPH_ECOLI Acyl carrier protein phosphodiesterase OS=Escherichia coli (strain K12) OX=83333 GN=acpH PE=1 SV=2  MNFLAHLHLAHLAESSLSGNLLADFVRGNPEESFPPDVVAGIHMHRRIDVLTDNLPEVREAREWFRSETRRVAPITLDVMWDHFLSRHWSQLSPDFPLQEFVCYAREQVMTILPDSPPRFINLNNYLWSEQWLVRYRDMDFIQNVLNGMASRRPRLDALRDSWYDLDAHYDALETRFWQFYPRMMAQASRKAL 

I know there are libraries which do the same thing, however, I had to write this script as a homework, without the help of BioPython or other similar libraries.

My question is: how can I make this code more efficient?

import sys import os   def readFasta( path ):          seq = { }     aCount = { }     alphabet = "ABCDEFGHIJKLMNOPQRSTUVWYZX*-"        for fileName in path:                  with open( fileName ) as file:             fastaLine = file.readlines( )                          # sequence storage                          for line in fastaLine:                 if line[ 0 ] == ">" :                     headerTitle = line.split( "|" )[ 1 ]                     seq[ headerTitle ] = ""                 else:                     seq[ headerTitle ] += line.strip( )              # aminoacid count              for id in seq:                 aCount[ id ] = { }                 for sequence in seq[ id ]:                     if sequence not in alphabet:                         print( "Check your sequence! Character: " + str(sequence) + " from sequence: " + id )                     for letter in alphabet:                         aCount[ id ][ letter ] = 0                          # count aminoacid occurence/sequence                          for name in seq:                 for char in seq[ name ]:                     if char in aCount[ name ]:                         aCount[ name ][ char ] += 1          # write to .csv file          outFile = open( "output.csv" , "w" )               outFile.write( "Name" + '\t' )          header = ""     for letter in alphabet:         header += letter + '\t'      outFile.write( header + "Total" )     outFile.write( '\n' )                    for name in aCount:         entry = ""         counter = 0         for letter in aCount[ name ]:             entry += str(aCount[ name ][ letter ]).strip( ) + '\t'             counter += aCount[ name ][ letter ]         outFile.write( name + '\t' + entry + '\t' + str( counter ) )         outFile.write( '\n' )  # files to list  entries = os.listdir( path )  # filter for .fasta/.fa files  new_list = [ ] for entry in entries:     if ".fasta" in entry:         new_list.append( entry )     elif ".fa" in entry:         new_list.append( entry )          # execute func  readFasta( new_list ) 
           
         
         

Lista de respuestas

5
 
vote
vote
La mejor respuesta
 

Además de los puntos planteados en las otras respuestas:

Importación extraña

La primera línea es arr9 , pero no veo forEach0 usado en cualquier lugar; Esto puede ir.

Naming explícito

En general, es mejor usar nombres explícitos. Por lo tanto, en lugar de solo forEach1 (que podría ser corto para forEach2 o forEach3 , por ejemplo) lo escribe. En este caso, parece que debe usar forEach4 . De manera similar, forEach5 no me dice lo que está contando, aparte del hecho de que hay un 998877766555443326 involucrado; solo llámalo forEach7 .

Naming consistente

Cuando está hablando del mismo objeto en dos lugares diferentes en el código, debe preferir usar nombres consistentes. Por ejemplo, utiliza forEach8 para el forEach9 Cuando lee el archivo, pero 99887766555443330 Para las mismas teclas en el siguiente bloque, y < Código> result.push(some_action(item))1 en el bloque después de eso. Una vez más, iría con un nombre más explícito como result.push(some_action(item))2 .

Objetos incorporados de sombreado

Hablando de result.push(some_action(item))3 , eso es en realidad una construido -En función . En general, es una mala idea cambiar el nombre de algo que está integrado en el idioma. Esto se llama "Shadowing" , y es posible romper el código al hacerlo, y en general se considera mala forma.

Mantener el archivo abierto más largo de lo que se necesita

Una vez que haya ejecutado result.push(some_action(item))4 , está terminado con el archivo, y puede cerrarlo, lo que significa que está listo con el bloque 99887766655443335 , y Usted puede indiar a los bloques después de eso. Por supuesto, como result.push(some_action(item))6 señaló, es probable que sea mejor procesar cada línea a medida que onese sobre ellos. (Esto es especialmente importante con archivos enormes, donde es posible que desee cargar una sola línea a la memoria, en lugar de cargar el archivo completo en la memoria). Por lo tanto, querrá que result.push(some_action(item))77 bloque Para parecer más así:

  result.push(some_action(item))8  

Tenga en cuenta que no he sangrado el comentario del conteo de aminoácidos (y todo lo siguiente).

Probablemente un bucle excesivo

Supongo que solo desea advertir al usuario sobre todos los personajes no reconocidos en una línea determinada, en lugar de cada aparición de todos estos caracteres. En ese caso, es mejor hacer un result.push(some_action(item))99 de todos los caracteres en la secuencia, y verifique si alguna no está en some_action(item)0 :

  some_action(item)1  

definitivamente excesivo de bucle

Al hacer some_action(item)2 , está en bucle sobre todos los caracteres en la secuencia. Si bien dudo que esto sea lo que desea para las líneas anteriores, esto definitivamente no es lo que desea al hacer some_action(item)3 ; Ese bucle debe estar sin sangría, de modo que se haya hecho una vez por cada some_action(item)4 .

Utilice el método 99887766555443345 para hacer su conteo

Python ya sabe cómo contar cuántas veces aparece un personaje en una cadena: eso es El some_action(item)6 MÉTODO . Así que en lugar de hacer un bucle sobre cada carácter en la cadena y agregar 1 al carácter apropiado, simplemente bucle sobre el alfabeto. Puede cortar ese bucle donde inicializa los conteos a 0, y simplemente averigüe los conteos. Poniendo esto junto con los elementos anteriores (incluido el nombre del nombre de algunas variables), tendrás algo como esto:

  some_action(item)7  

y ese bucle entero bajo some_action(item)8 se puede eliminar. Esto será mucho más rápido.

Uso some_action(item)9 Bloque para escribir archivo, también

Usé correctamente un bloque forEach0 para leer los archivos; Úsalo cuando escribe el archivo CSV, también:

  forEach1  

(y sangrar lo que sigue y usa forEach2 ).

No hay necesidad de agregar cadenas explícitas

Cuando escribe forEach3

, puede, así que también podría escribir forEach4 . De manera similar n escribes
  forEach5  

También podría escribir

  forEach6  

 

In addition to the points raised in the other answers:

Extraneous import

The first line is import sys, but I don't see sys used anywhere; this can go.

Explicit naming

It's generally better to use explicit names. Thus, rather than just seq (which might be short for sequence or sequences, for example) spell it out. In this case, it looks like you should use sequences. Similarly, aCounts doesn't tell me what you're counting, other than the fact that there's an a involved; just call it amino_acid_counts.

Consistent naming

When you're talking about the same object in two different places in the code, you should prefer to use consistent names. For example, you use headerTitle for the seq key when reading the file in, but id for the same keys in the next block, and name in the block after that. Again, I would go with a more explicit name like sequence_id.

Shadowing built-in objects

Speaking of id, that's actually a built-in function. It's generally a bad idea to rename something that's built in to the language. This is called "shadowing", and it's possible to break code by doing it xe2x80x94 and generally considered bad form.

Keeping the file open longer than it's needed

Once you've run fastaLine = file.readlines( ), you're done with the file, and you can close it xe2x80x94 meaning that you're done with the with open block, and you can un-indent the blocks after it. Of course, as Reinderien pointed out, it's probably better to just process each line as you iterate over them. (This is especially important with huge files, where you might want to just load a single line at a time into memory, rather than loading the entire file into memory.) So you'll want that with open block to look more like this:

        # sequence storage         with open(path, "r") as file:             for line in file:                 if line[0] == ">" :                     sequence_id = line.split("|")[1]                     sequences[sequence_id] = ""                 else:                     sequences[sequence_id] += line.strip()          # amino acid count 

Note that I've un-indented the amino acid count comment (and everything following it).

Probably excessive looping

I'm guessing that you just want to warn the user about all unrecognized characters in a given line, rather than every single occurrence of every such character. In that case, it's better to make a set of all the characters in the sequence, and check if any are not in alphabet:

sequence_characters = set(seq[id]) if not sequence_characters.issubset(alphabet):     print(         "Check your sequence! Extra characters: '" + str(sequence_characters - alphabet)         + "' from sequence: " + str(sequence_id)     ) 

Definitely excessive looping

When doing for sequence in seq[ id ], you're looping over every character in the sequence. While I doubt that this is what you want for the previous lines, this definitely is not what you want when doing for letter in alphabet; that loop should be un-indented, so that it's just done once for each id.

Use the built-in str.count method to do your counting

Python already knows how to count how many times a character appears in a string: that's the str.count method. So rather than looping over every character in the string and adding 1 to the appropriate character, just loop over the alphabet. You can cut out that loop where you initialize the counts to 0, and just figure out the counts. Putting this together with the previous items (including renaming some variables), you'll have something like this:

        # amino acid count         for sequence_id, sequence in sequences.items():             amino_acid_counts[sequence_id] = { }             sequence_characters = set(sequence)             if not sequence_characters.issubset(alphabet):                 print(                     "Check your sequence! Extra characters: '" + str(sequence_characters - alphabet)                     + "' from sequence: " + str(sequence_id)                 )             for letter in alphabet:                 amino_acid_counts[sequence_id][letter] = sequence.count(letter) 

And that whole loop under # count aminoacid occurence/sequence can be removed. This will be much faster.

Use with open block for writing file, too

You correctly used a with open block for reading the files; use it when you write the CSV file, too:

    with open("output.csv", "w") as outFile: 

(and indent whatever follows and uses outFile).

No need to add explicit strings

When you write "Name" + '\t', you could just as well write "Name\t". Similarly, when you write

    outFile.write( header + "Total" )     outFile.write( '\n' ) 

you could just as well write

    outFile.write(header + "Total\n") 
 
 
       
       
8
 
vote

¡Bienvenido a la revisión del código!

Añadiré a la otra respuesta de Reinderien.

PEP-8

En Python, es común (y recomendado) seguir la guía de estilo PEP-8 para escribir un código limpio, sostenible y consistente.

Las funciones y las variables deben ser nombradas en un lower_snake_case , CLASES COMO UpperCamelCase y constantes como UPPER_SNAKE_CASE .

No se necesitan espacios en blanco innecesarios en torno a los argumentos al definir o llamar a una función. Esto se aplica a los elementos de la matriz / lista de referencias también.

Funciones

Dividir su código en funciones más pequeñas individuales, haciendo tareas singulares. Unos pocos ejemplos serían, leyendo / analizando los archivos FASTA, validando la secuencia de genes, contando ocurrencias (intente 99887776655544333 paquete para hacer esto).

CSV / TSV

Python viene con el paquete incorporado csv que también se puede usar para escribir archivos TSV. Usted no necesita modificar / escribir cada línea usted mismo.

Glob Búsqueda de archivos

Python 3+ tiene otro paquete incorporado pathlib , que admite obtener todos los archivos con un patrón de globo. Usted nuevamente, no será necesario agregar manualmente (metafóricamente) todos los archivos con .fa o .fasta extensiones.

if __name__ bloque

Para los scripts, es una buena práctica colocar su característica ejecutable dentro de la cláusula if __name__ == "__main__" .

 

Welcome to Code Review!

I'll add to the other answer from Reinderien.

PEP-8

In python, it is common (and recommended) to follow the PEP-8 style guide for writing clean, maintainable and consistent code.

Functions and variables should be named in a lower_snake_case, classes as UpperCamelCase, and constants as UPPER_SNAKE_CASE.

No unnecessary whitespaces are needed around the arguments when defining or calling a function. This applies for referring array/list elements as well.

Functions

Split your code into individual smaller functions, doing singular tasks. A few examples would be, reading/parsing the fasta files, validating sequence of gene, counting occurrences (try collections.Counter package to do this).

CSV/TSV

Python comes with inbuilt package csv which can also be used to write tsv files. You do not need to modify/write each line yourself.

Glob searching for files

Python 3+ has another inbuilt package pathlib, which supports getting all files using a glob pattern. You would again, not need to manually (metaphorically) aggregate all the files with .fa or .fasta extensions.

if __name__ block

For scripts, it is a good practice to put your executable feature inside the if __name__ == "__main__" clause.

 
 
   
   
7
 
vote

Ruta?

seguramente UpperCamelCase0 no es una sola ruta, ya que lo realizó a través de él. Así que, al menos, esto está mal llamado y debe ser UpperCamelCase1 . La variable que itera a través de ella, UpperCamelCase2 , debe ser UpperCamelCase3 ; De manera similar, para su otra función y nombres de variables.

ITERACIÓN DE LA LÍNEA

En lugar de llamar UpperCamelCase4 , simplemente iterar sobre UpperCamelCase5 en sí mismo, que tendrá el mismo efecto.

Alfabeto

¿Por qué está fuera de servicio - UpperCamelCase6 ?

redundante UpperCamelCase7

  UpperCamelCase8  

puede colapsar el UpperCamelCase9 a

  UPPER_SNAKE_CASE0  

Si mira el patrón de la substrición, el segundo incluye el primero. Además, no agregue espacios en el interior de sus parentes.

 

Path?

Surely path is not a single path, since you loop through it. So at the least, this is poorly-named and should be paths. The variable you iterate through it, fileName, should be file_name; similarly for your other function and variable names.

Line iteration

Rather than calling readlines(), simply iterate over file itself, which will have the same effect.

Alphabet

Why is it out of order - WYZX?

Redundant if

if ".fasta" in entry:     new_list.append( entry ) elif ".fa" in entry:     new_list.append( entry ) 

can collapse the if to

if '.fa' in entry:     new_list.append(entry) 

If you look at the substring pattern, the second includes the first. Also, don't add spaces on the inside of your parens.

 
 
 
 

Relacionados problema

2  Una secuencia de errores  ( A sequence of mistakes ) 
Esta pregunta es parte de una serie que resuelve la Rosalind desafíos. Para la pregunta anterior en esta serie, consulte el código genético . El repositor...

4  Vectorizamos la prueba exacta de Fisher  ( Vectorize fishers exact test ) 
Tengo dos marcos de datos / listas de datos, humanSplit 9988776655544331 , y son del formulario > ratSplit$Kidney_F_GSM1328570 ratGene ratRepli...

2  Compara secuencia y encabezados de mapas en el archivo FASTA  ( Compare sequence maps headers in fasta file ) 
Este es el código PERL que compara la secuencia en FASTA FILE & AMP; Mapea el encabezado. Aunque el código está funcionando bien, todavía me gustaría hacerlo ...

7  Genomo de la cadena de clump para encontrar problema  ( Genome string clump finding problem ) 
Estoy tratando de resolver los problemas de bioinformática de un Curso Stepic . El problema planteado : encontrar grupos del mismo patrón dentro de un gen...

2  Tarea de procesamiento de datos para bioinformática  ( Data processing task for bioinformatics ) 
Hoy lanzé este programa C para manejar una tarea de procesamiento de datos bioinformáticos. El programa parece funcionar correctamente, pero quería saber si a...

10  Una clase de C ++ simple para empacar las cadenas de ADN  ( A simple c class for packing dna strings ) 
Introducción Tengo este pequeño programa C ++ que empaca una cadena de ADN sobre el alfabeto ACGT en un vector de bit, dos bits por carácter. Código ...

4  Encontrar un motivo de proteína  ( Finding a protein motif ) 
He resuelto los bits carnos de este problema bioinformático , aunque un poco torpemente creo . En particular, me he metido a la manipulación de la informació...

9  Analizando las etiquetas genéticas  ( Analyzing genetic tags ) 
He ido de ida y vuelta unas cuantas veces recientemente en mi estilo de codificación de Perl cuando se trata de las subrutinas del módulo. Si tiene un objeto ...

11  Cálculo de la probabilidad de conjunta de los eventos de N desde una secuencia de muestras de ocurrencias  ( Calculating the joint probability of n events from a sample sequence of occurren ) 
Estoy escribiendo un algoritmo para tomar una lista de muestras de secuencias de eventos, calcule las probabilidades de transición de 1 paso de las secuencias...

1  Salida de explosión en formato XML utilizando expresión regular  ( Parsing blast output in xml format using regular expression ) 
Hay muchas otras formas mejores de analizar la salida de explosión en formato .xml, pero tenía curiosidad por intentar usar la regex, incluso si no es tan sen...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos