2010-07-14 69 views
5

Tengo un gran archivo de texto de miles de hudred. Tengo que extraer 30,000 líneas específicas que están todas en el archivo de texto en lugares al azar. Este es el programa que tengo que extraer una línea a la vez:¿Extrae líneas de texto específicas?

big_file = open('C:\\gbigfile.txt', 'r') 
small_file3 = open('C:\\small_file3.txt', 'w') 
for line in big_file: 
    if 'S0414' in line: 
     small_file3.write(line) 
gbigfile.close() 
small_file3.close() 

¿Cómo puedo acelerar este proceso para 30.000 líneas que tengo que mirar hacia arriba>?

+1

Creo que todas las respuestas hasta ahora no entienden bien su pregunta. Si lo entiendo, no estás buscando acelerar tu ciclo. En cambio, tiene 30,000 cadenas similares a 'S0414', y quiere encontrar una manera de encontrar cada ocurrencia de cada una. Es esto lo que estás buscando? – Wilduck

+0

Necesito encontrar S0414 y GT213 y AT3423 y PR342 y hay 30,000 cosas diferentes que necesito encontrar. ¿Puedo encontrar los 30,000 a la vez sin hacer que este programa tenga una línea para cada uno de los nombres que necesito encontrar? THEN mi programa tendrá 30,000 líneas, lo cual es problemático. – novak

+0

¿'S0414' y' GT213', etc. aparecen en el mismo lugar del bigfile como Nick pregunta? http://stackoverflow.com/questions/3248395/extract-specific-text-lines/3248603#3248603 –

Respuesta

5

Aha! Entonces su verdadero problema es cómo probar muchas condiciones por línea y si una de ellas está satisfecha, generar esa línea. Más fácil va a utilizar expresiones regulares, me cree:

import re 
keywords = ['S0414', 'GT213', 'AT3423', 'PR342'] # etc - you probably get those from some source 
pattern = re.compile('|'.join(keywords)) 

for line in inf: 
    if pattern.search(ln): 
     outf.write(line) 
+0

Esta es la forma correcta de hacerlo. Gracias por señalar lo equivocada que estaba mi respuesta. Sin embargo, es posible que desee echar un vistazo a las preguntas pasadas de @novak para ver por qué intenté ir con la sintaxis más simple posible. Específicamente mi respuesta a esta http://stackoverflow.com/questions/3207719/question-about-splitting-a-large-file. Mejor, Wilduck. – Wilduck

+0

Esto ha funcionado bien gracias. – novak

0

Si la línea comienza con S0414, entonces usted podría utilizar el .startswith método:

if line.startswith('S0414'): small_file3.write(line) 

También podría Elimina espacios en blanco izquierda, si hay alguna:

line.lstrip().startswith('S0414') 

Si 'S0414' siempre después un cierto punto, por ejemplo, siempre tiene al menos 10 caracteres y nunca en los últimos 5 caracteres, puede hacer:

'S0414' in line[10:-5] 

De lo contrario, tendrá que buscar en cada línea, como usted.

+0

todos comienzan con nombres diferentes – novak

1

De acuerdo con la documentación de Python de file objects, la iteración que se realiza no debe ser especialmente lenta, y la búsqueda de subcadenas también debería ser adecuada para la velocidad.

No veo ningún motivo por el que el código deba ser lento, por lo que si necesita que sea más rápido, puede que tenga que volver a escribirlo en C y usar mmap() para acceder rápidamente al archivo fuente.

+1

Quiero saber cómo puedo buscar las líneas que quiero no una a la vez. Lke busca los 30,000 a la vez. – novak

1

1. Trate de leer todo el archivo

Una velocidad se puede hacer es leer todo el archivo en la memoria si es posible, de lo leído en trozos. Usted dijo 'varios hudred mil líneas' Digamos que 1 millón de líneas con cada línea 100 Char es decir, alrededor de 100 MB, si usted tiene esta cantidad de memoria libre (supongo que tienes) acaba de hacer esto

big_file = open('C:\\gbigfile.txt', 'r') 
big_file_lines = big_file.read_lines() 
big_file.close() 
small_file3 = open('C:\\small_file3.txt', 'w') 
for line in big_file_lines: 
    if 'S0414' in line: 
     small_file3.write(line) 
small_file3.close() 

Tiempo esto con orginal versión y ver si hace la diferencia, creo que lo hará.

Pero si su archivo es realmente grande en GBs, entonces puede leerlo en fragmentos, p. Ej. 100 MB trozos, divídalo en líneas y busque pero no olvide unir líneas en cada intervalo de 100MB (puedo elaborar más si este es el caso)

file.readlines devuelve una lista que contiene todas las líneas de datos en el archivo . Si se le da un parámetro opcional sizehint, lee tantos bytes del archivo y más para completar una línea, y devuelve las líneas de eso. Esto se usa a menudo para permitir la lectura eficiente de un archivo grande por líneas, pero sin tener que cargar todo el archivo en la memoria. Solo se devolverán las líneas completas.

Consulte también el siguiente enlace para ver la diferencia de velocidad entre línea por línea y la lectura de todo el archivo. http://handyfloss.wordpress.com/2008/02/15/python-speed-vs-memory-tradeoff-reading-files/

2.Trate de escribir el archivo de toda

También puede escribir la línea tienda y ellos a la vez al final, aunque no estoy seguro de si ayudará mucho

big_file = open('C:\\gbigfile.txt', 'r') 
big_file_lines = big_file.read_lines() 
small_file_lines = [] 
for line in big_file_lines: 
    if 'S0414' in line: 
     small_file_lines.append(line) 
small_file3 = open('C:\\small_file3.txt', 'w') 
small_file3.write("".join(small_file_lines)) 
small_file3.close() 

3. filtro tratan de

También puede intentar utilizar el filtro, en lugar del lazo, ver si hace la diferencia

small_file_lines= filter(lambda line:line.find('S0414') >= 0, big_file_lines) 
+0

Nada de esto ayudará, lo siento. No tiene sentido el # 1 y el # 2, ya que no hay beneficio de almacenar las líneas de entrada o salida; esta es una secuencia, usted conserva o rechaza una línea y no necesita previa o siguiente para tomar una decisión , ni tienes que ordenarlo # 3 ralentizará el procesamiento debido a la adición artificial de invocaciones de lambda fn por cada línea * y * construcción de la lista de resultados –

+0

@Nas Banov, ¿lo ha calculado usted? Diré que traten de cargar un archivo completo en la memoria y lo lean línea por línea, y verán mucha diferencia. –

+0

le invitamos a mostrar los datos que respaldan sus reclamaciones –

0

¿Cuáles son los criterios que definen las 30000 líneas que quieres extraer? Cuanta más información proporciones, más probabilidades tendrás de obtener una respuesta útil.

Si quiere todas las líneas que contienen una cierta cadena, o más generalmente que contiene cualquiera de un conjunto de cadenas dado, o una aparición de una expresión regular, use grep. Es probable que sea significativamente más rápido para grandes conjuntos de datos.

0

Esto me recuerda un problema descrito por Tim Bray, que intentó extraer datos de los archivos de registro del servidor web utilizando máquinas multinúcleo. Los resultados se describen en The Wide Finder Project y Wide Finder 2. Por lo tanto, si las optimizaciones seriales no son lo suficientemente rápidas para usted, este puede ser un buen lugar para comenzar. Hay ejemplos de este tipo de problema en muchos idiomas, including python. cita clave de ese último eslabón:

Resumen

En este artículo, se tomó una implementación de Python relativamente rápido y optimizado, usando un número de trucos:

  • -Pre compilado RE patrones
  • filtrado rápido de líneas candidatos
  • Chunked lectura
  • Multi PLE procesa
  • Asignación de memoria, en combinación con el apoyo para las operaciones de RE en tampones mapeadas

Esto redujo el tiempo necesario para analizar 200 megabytes de datos de registro de 6.7 segundos a 0.8 segundos en la máquina de prueba. O en otras palabras, la versión final es más de 8 veces más rápida que la versión original de Python, y (potencialmente) 600 veces más rápida que la versión original de Tim de de Tim.

Una vez dicho esto, 30.000 líneas no es que muchos por lo que puede al menos de inicio mediante la investigación de su lectura de disco/escritura. ¿Ayuda si escribe el resultado en algo que no sea el disco desde el que está leyendo la entrada o si lee todo el archivo de una vez antes de procesarlo?

1

Puede intentar leer en bloques grandes y evitar la sobrecarga de división de línea, excepto las líneas de interés específicas. Por ejemplo, suponiendo que ninguna de sus líneas es más largo que un megabyte:

BLOCKSIZE = 1024 * 1024 

def byblock_fullines(f): 
    tail = '' 
    while True: 
     block = f.read(BLOCKSIZE) 
     if not block: break 
     linend = block.rindex('\n') 
     newtail = block[linend + 1:] 
     block = tail + block[:linend + 1] 
     tail = newtail 
     yield block 
    if tail: yield tail + '\n' 

esto toma un argumento de archivo abierto y los rendimientos de los bloques de alrededor de 1 MB garantizado para terminar en una línea.Para identificar (iterador-wise) todas las apariciones de una cadena de aguja dentro de una cadena pajar:

def haystack_in_needle(haystack, needle): 
    start = 0 
    while True: 
     where = haystack.find(needle, start) 
     if where == -1: return 
     yield where 
     start = where + 1 

Para identificar todas las líneas pertinentes dentro de un bloque de este tipo:

def wantlines_inblock(s, block): 
    last_yielded = None 
    for where in haystack_in_needle(block, s): 
     prevend = block.rfind('\n', where) # could be -1, that's OK 
     if prevend == last_yielded: continue # no double-yields 
     linend = block.find('\n', where) 
     if linend == -1: linend = len(block) 
     yield block[prevend + 1: linend] 
     last_yielded = prevend 

Cómo esta todo encaja:

def main(): 
    with open('bigfile.txt') as f: 
     with open('smallfile.txt', 'w') as g: 
      for block in byblock_fulllines(f): 
       for line in wantlines_inblock('S0414', block) 
        f.write(line) 

En 2.7 puede doblar las dos declaraciones with en una, solo para reducir la anidación un poco.

Nota: este código no se ha probado, por lo que podría haber (afortunadamente pequeños) errores como por ejemplo uno por uno. El rendimiento necesita el ajuste del tamaño del bloque y debe calibrarse mediante la medición en su máquina y datos específicos. Su experiencia puede ser diferente. No válido donde lo prohíbe la ley.

+0

¿La implementación de Python I/O y/o la biblioteca de C subyacente no leerán grandes bloques debajo del capó? Creo que hacerlo de manera explícita en Python sería más lento, aunque confieso que no lo he comparado. –

+0

Tenga en cuenta que el problema descrito es diferente: necesita filtrar el archivo según criterios ~ 30000 * diferentes *, no en el único 'S0414'. Eso cambia el juego –

+0

@Daniel, puede especificar el almacenamiento en búfer, pero luego cada búfer se dividirá en todas las líneas que están en él, la mayoría de las cuales serán irrelevantes; Estoy intentando ahorrar esa parte del costo, solo aislar las líneas relevantes. Eso solo ahorrará tiempo si una porción muy pequeña de las líneas es relevante, por supuesto. –

0

La mejor apuesta para acelerarlo sería si la cadena específica S0414 siempre aparece en la misma posición de carácter, entonces, en lugar de tener que hacer varias comparaciones fallidas por línea (dijiste que comienzan con diferentes nombres) podría hacer uno y listo

p. Ej. si estás archivo tiene líneas como

GLY S0414 GCT 
ASP S0435 AGG 
LEU S0432 CCT 

hacer un if line[4:9] == 'S0414': small.write(line).

2

Pruebas de distintas condiciones por línea es generalmente lento cuando se utiliza un algoritmo ingenuo. Existen varios algoritmos superiores (por ejemplo, usando Tries) que pueden ser mucho mejores. Te sugiero que le des una oportunidad al Aho–Corasick string matching algorithm. Ver here para una implementación de Python. Debería ser considerablemente más rápido que el enfoque ingenuo de usar un ciclo anidado y probar cada cadena individualmente.

+0

Creo que su respuesta es interesante, pero tenga en cuenta que probablemente sea contraproducente para un principiante, lo que OP parece ser. Por cierto, no las funciones regex, ya sea Python o C lib, ya contienen algoritmos optimizados (digamos Boyer-Moore)? –

+1

@Nas: Si bien admito que la implementación de este algoritmo es un tema avanzado, me vinculé a una biblioteca relativamente amigable que debería ser utilizable por un novato. Por otro lado, no asumiría que una expresión regular tiene tales optimizaciones, ya que este tipo de tipo de datos (es decir, Tries) está optimizado específicamente para búsquedas que tienen muchas consultas de búsqueda simultáneas. Esperaría que se implementara una expresión regular en términos de una máquina de estado, que probablemente no sea tan buena con una estrategia como ''|'. Join (palabras clave)' ... pero obviamente la mejor estrategia es probar ambas soluciones y ver cual es mas rapido – Brian

0

Este método asume los valores especiales aparecen en la misma posición en la línea de gbigfile

def mydict(iterable): 
    d = {} 
    for k, v in iterable: 
     if k in d: 
      d[k].append(v) 
     else: 
      d[k] = [v] 
    return d 

with open("C:\\to_find.txt", "r") as t: 
    tofind = mydict([(x[0], x) for x in t.readlines()]) 

with open("C:\\gbigfile.txt", "r") as bigfile: 
    with open("C:\\outfile.txt", "w") as outfile: 
     for line in bigfile: 
      seq = line[4:9] 
      if seq in tofind[seq[0]]: 
       outfile.write(line) 

Dependiendo de lo que la distribución de la carta a partir de esos objetivos se puede cortar sus comparaciones por una cantidad significativa. Si no sabe dónde aparecerán los valores, está hablando de una operación LONG porque tendrá que comparar cientos de miles, digamos 300,000 a 30,000 veces. Eso es 9 millones de comparaciones que tomarán un largo tiempo.

+0

Hmm, sospecho que usar 'set()' simple en lugar de este diccionario de listas de primer orden de elaboración casera será más rápido. –

Cuestiones relacionadas