Re: [Python-es] Buscar, leer y escribir archivos grandes con Python
Hola. Me pareció muy interesante este hilo y he hecho unas cuantas pruebas con el modulo IO de python3 Y en promedio me procesa un archivo de 70Mb en 1 segundo. El código de la prueba: http://pastii.com.ar/1819 Andrei. ;) El 4 de junio de 2011 04:18, Manuel Enrique González Ramírez maeng...@gmail.com escribió: El 3 de junio de 2011 14:19, Alexis Roda alexis.roda.villalo...@gmail.com escribió: En/na Kiko ha escrit: En el programa que mandaste en el primer mensaje parece implícito que la longitud del código es siempre de 9 caracteres. Esto hace innecesario el uso de rsplit o rfind. def procesa(entrada, salida) : with open(entrada,'r') as input: with open(salida,'w') as output: for l in input : output.write(l[-9:]) In [1]: l = 1|2011-05-20 23:08:56|122711527|OPERADOR1|HOST TOHOST|212454|1|HOST CLIENTE|192630167 In [2]: timeit l.rsplit(|, 1)[-1] 100 loops, best of 3: 827 ns per loop In [3]: timeit l[l.rindex(|)+1:] 100 loops, best of 3: 676 ns per loop In [4]: timeit l[l.rfind(|)+1:] 100 loops, best of 3: 682 ns per loop In [5]: timeit l[-9:] 100 loops, best of 3: 208 ns per loop Fíjate en que utilizar rfind (o rindex) como hacías originalmente es mas eficiente que utilizar rsplit. La explicación es que rsplit crea dos cadenas (la mitad izquierda y la derecha), una lista con las dos cadenas y finalmente accede al último elemento de la lista. En la versión con rindex se crea directamente la cadena derecha. Como curiosidad, en el caso de utilizar un método de l podrías arañar unos nanosegundos haciendo algo como: In [6]: rf=l.rfind In [7]: timeit l[rf(|)+1:] 100 loops, best of 3: 602 ns per loop con esto evitas que el interprete tenga que buscar el método rfind de l en cada iteración. Claramente, rsplit parece que funciona mejor. He hecho mi función (abrekiko) con una list comprehesion y también va un poquito más lenta que con el for a pelo y tenía entendido que usar list comprehensions era más efectivo. Las list comprehension sirven para crear listas. Lo que hace tu código es equivalente a: def abrelasi(entrada,salida): lista = [] with open(entrada,'r') as input: with open(salida,'w') as output: for l in input : lista.append(output.write(l.rsplit(',',1)[-1])) Al final lista contiene 30 valores None. No le veo el sentido. ¿Alguien tiene formas más rápidas de lectura de ficheros de texto? Yo hubiese utilizado cut -d\| -f9 entrada.txt salida.txt Como ya te han dicho debes considerar el tiempo de desarrollo mas el tiempo de ejecución y valorar si el esfuerzo extra vale la pena. Dicho esto, si quieres aprender, te recomendaría probar el módulo cProfile: http://docs.python.org/library/profile.html#module-cProfile este modulo mide el tiempo de ejecución del programa y te ayudará a descubrir donde está el cuello de botella. Tal vez estés dedicando horas a optimizar algo que luego no tiene un impacto decisivo en el tiempo total de ejecución. En el caso de procesar el archivo tengo la impresión de que el problema estará en la E/S y no el el procesamiento de los datos en si (utilizando un programa razonable). Saludos ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/ Excelentísimos aportes de todos, si que se aprende!! De verdad muuchas gracias, he seguido el hilo, cambiado el código y he obtenido excelentes resultados. Estos aportes están de lo máximo y si continúa bienvenido sea el aprendizaje -- Manuel Enrique González Ramírez http://maengora.blogspot.com ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/ -- http://twitter.com/andsux http://www.niwi.be http://www.freebsd.org/ http://www.postgresql.org/ http://www.python.org/ http://www.djangoproject.com/ Linux is for people who hate Windows, BSD is for people who love UNIX Social Engineer - Because there is no patch for human stupidity ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/
Re: [Python-es] Buscar, leer y escribir archivos grandes con Python
El día 3 de junio de 2011 12:39, Kiko kikocorre...@gmail.com escribió: Usando ipython defino tres funciones: def abrekiko(entrada, salida): input = open(entrada, 'r') output = open(salida, 'w') for l in input: output.write(l.split(',')[-1]) input.close() output.close() def abrejoe(entrada,salida): with open(entrada,'r') as input: with open(salida,'w') as output: [output.write(l.split(',')[-1]) for l in input] def abrelasi(entrada,salida): with open(entrada,'r') as input: with open(salida,'w') as output: [output.write(l.rsplit(',',1)[-1]) for l in input] Los resultados que me salen son: In [15]: timeit abrekiko(entrada,salida) 1 loops, best of 3: 735 ms per loop In [16]: timeit abrejoe(entrada,salida) 1 loops, best of 3: 766 ms per loop In [17]: timeit abrelasi(entrada,salida) 1 loops, best of 3: 563 ms per loop Mi entrada es un fichero con 300.000 líneas y 8 columnas separadas por coma. Claramente, rsplit parece que funciona mejor. He hecho mi función (abrekiko) con una list comprehesion y también va un poquito más lenta que con el for a pelo y tenía entendido que usar list comprehensions era más efectivo. list comprehensions genera una lista con todos los datos de la iteración, lo cual no es muy bueno para el uso de la memoria. Usando generadores (como list comprehensions pero usando parentesis en vez de corchetes) se consume menos memoría, lo cual también es bueno. Respecto a lo de que vaya un poquito más rápido o un poquito más lento puede ser por temas varios. Si ejecutas el test dos veces verás que hay cierta desviación, por lo que pequeñas mejoras no son representativas. ¿La diferencia de rsplit con respecto a split es solo que empieza el split por la derecha? Si, esa es la única diferencia. Pero el truquito en cuestión está en el segundo parámetro. Ese 1 le indica que pare al cortar un elemento. En este caso concreto solo queremos el último, por lo que no tiene demasiado sentido seguir buscando en la cadena y troceando el resto de los campos. Aparte del recorrido en la cadena, la creación de esas nuevas cadenas para representar cada uno de los capos no es gratis. ¿Alguien tiene formas más rápidas de lectura de ficheros de texto? Respuesta corta: Si, no, a veces. Respuesta larga (me pongo en modo chapas así que dejar de leer si se quiere): Moraleja 1: Si el cuello de botella es el acceso a disco poco hay por hacer Un día me puse hacer una guerra de lenguajes implementando un wc (programa para contar lineas) muy simple en c, haskell y python. En C las pasé canutas porque la función readline del estandar C99 no estaba en la librería estandar del OSX, por lo que tuve que hacer una implementación que hiciera uso de buffers. Quedó un código muy largo y me costo cierto tiempo hacerlo. En haskell, quizá por ser un lenguaje que tampoco domino, me quedó un programa corto y eficiente, pero cada caracter del codigo había que pensarlo con cautela. La forma de hacer el plegado de funciones para que la evaluación perezosa no fuese un problema, la forma de que la lectura usará bien buffers, ... todo eso hizo que me costara horrores escribir unas pocas lineas de código. En python el programa era ligeramente más largo que haskell, pero me salió solito y sin pensarlo mucho. Los resultados fueron curiosos. Cuando el fichero no estaba en cache de disco, el cuello de botella era el acceso a este, por lo que los tres códigos tardaban lo mismo en ejecutarse. Estando el fichero en cache (cosa que tampoco sería demasiado normal, supongo) la versión c y haskell eran mucho más rápidas que la versión en python. La diferencia entre c y haskell no era muy grande siendo la primera algo más rápida. Moraleja 2: Depende de la estructura de ese fichero de texto. Si en vez de tener que leer el último campo en el ejemplo de este correo, se hubiera tenido que leer uno de en medio la optimización realizada sería menos evidente. Si el fichero tiene alguna cadena larga fácil de buscar, algoritmos como el de Boyer-Moore[1] pueden ayudar bastante. Si el fichero en vez del tipo csv fuese uno con campos fijos, se podría hacer un mmap y acceder directamente a las zonas de memoria donde estan los datos, evitando un recorrido sobre el fichero. Si el trabajo a hacer sobre el fichero es pesado (cuello de botella en cpu) se puede probar a parelizar con multiproceso. Eso si, a veces la sincrinización/comunicación entre procesos puede quitar más rendimiento del que aporta. Si ... Por desgracia no conozco recetas mágicas para usar en todos los casos. Pero si que hay algunas herramientas que se pueden tener en reserva para aplicar en casos concretos. De ahí que la respuesta corta fuese si, no, a veces ;-) [1] http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm Moraleja 3: No centrarse solo en la ejecución del código. Si el objetivo es tener el resultado de una ejecución concreta, desde que eres
Re: [Python-es] Buscar, leer y escribir archivos grandes con Python
Respuesta corta: Si, no, a veces. Me imaginaba algo así :-D Muchas gracias por la extensión de la explicación. Exploraré mmap (aunque creo que para lo que lo estoy pensando ahora será menos útil) y multiprocesos y lo que pille por ahí. Como bien has dicho, no merece la pena programar dos horas para 2s de proceso. Pero esos 2s los tengo que repetir muchas veces todos los días. Si no consigo mejorar nada por lo menos habré aprendido algo (o no, que soy muy zoquete). ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/
Re: [Python-es] Buscar, leer y escribir archivos grandes con Python
En/na Kiko ha escrit: En el programa que mandaste en el primer mensaje parece implícito que la longitud del código es siempre de 9 caracteres. Esto hace innecesario el uso de rsplit o rfind. def procesa(entrada, salida) : with open(entrada,'r') as input: with open(salida,'w') as output: for l in input : output.write(l[-9:]) In [1]: l = 1|2011-05-20 23:08:56|122711527|OPERADOR1|HOST TOHOST|212454|1|HOST CLIENTE|192630167 In [2]: timeit l.rsplit(|, 1)[-1] 100 loops, best of 3: 827 ns per loop In [3]: timeit l[l.rindex(|)+1:] 100 loops, best of 3: 676 ns per loop In [4]: timeit l[l.rfind(|)+1:] 100 loops, best of 3: 682 ns per loop In [5]: timeit l[-9:] 100 loops, best of 3: 208 ns per loop Fíjate en que utilizar rfind (o rindex) como hacías originalmente es mas eficiente que utilizar rsplit. La explicación es que rsplit crea dos cadenas (la mitad izquierda y la derecha), una lista con las dos cadenas y finalmente accede al último elemento de la lista. En la versión con rindex se crea directamente la cadena derecha. Como curiosidad, en el caso de utilizar un método de l podrías arañar unos nanosegundos haciendo algo como: In [6]: rf=l.rfind In [7]: timeit l[rf(|)+1:] 100 loops, best of 3: 602 ns per loop con esto evitas que el interprete tenga que buscar el método rfind de l en cada iteración. Claramente, rsplit parece que funciona mejor. He hecho mi función (abrekiko) con una list comprehesion y también va un poquito más lenta que con el for a pelo y tenía entendido que usar list comprehensions era más efectivo. Las list comprehension sirven para crear listas. Lo que hace tu código es equivalente a: def abrelasi(entrada,salida): lista = [] with open(entrada,'r') as input: with open(salida,'w') as output: for l in input : lista.append(output.write(l.rsplit(',',1)[-1])) Al final lista contiene 30 valores None. No le veo el sentido. ¿Alguien tiene formas más rápidas de lectura de ficheros de texto? Yo hubiese utilizado cut -d\| -f9 entrada.txt salida.txt Como ya te han dicho debes considerar el tiempo de desarrollo mas el tiempo de ejecución y valorar si el esfuerzo extra vale la pena. Dicho esto, si quieres aprender, te recomendaría probar el módulo cProfile: http://docs.python.org/library/profile.html#module-cProfile este modulo mide el tiempo de ejecución del programa y te ayudará a descubrir donde está el cuello de botella. Tal vez estés dedicando horas a optimizar algo que luego no tiene un impacto decisivo en el tiempo total de ejecución. En el caso de procesar el archivo tengo la impresión de que el problema estará en la E/S y no el el procesamiento de los datos en si (utilizando un programa razonable). Saludos ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/
[Python-es] Buscar, leer y escribir archivos grandes con Python
Hola a tod@s, Tengo la siguiente situación: Un archivo de 29Mb que contiene 281.351 lineas. El archivo tiene la siguiente estructura: 1|2011-05-20 23:08:56|122711527|OPERADOR1|HOST TO HOST|212454|1|HOST CLIENTE|192630167 2|2011-05-20 23:09:08|122711530|OPERADOR12|HOST TO HOST|57212454|1000|HOST CLIENTE|192630168 3|2011-05-20 23:09:42|122711538|OPERADOR1|HOST TO HOST|454545454545|2000|HOST CLIENTE|192630169 4|2011-05-20 23:10:03|122711544|OPERADOR1|HOST TO HOST|121221211|2|HOST CLIENTE|192630170 5|2011-05-20 23:10:09|122711547|OPERADOR1|HOST TO HOST ESPECIAL|4545|5000|HOST CLIENTE|192630171 6|2011-05-20 23:10:46|122711554|OPERADOR1|HOST TO HOST ESPECIAL|545454445|5000|HOST CLIENTE|192630172 . . Del cual debo capturar la última sección, es decir, el número que me representa una secuencia (192630167, 192630168, etc). Lo hago así: fc = open('archivo.txt','r') for linea in fc: b = linea.rfind('|') posi = b + 1 posf = posi + 9 secuenciac = linea[posi:posf] print secuenciac Esto tarda en promedio 3 minutos (que es demasiado comparado con el tiempo que tarda [1min en promedio] con el script que me pasaron en Perl). Ahora, para sumarle al problema del tiempo que tarda dicha proceso, el resultado del ciclo anterior debe escribirse en otro archivo (datos.csv); eso lo hago así: # Abro un archivo para edición fdif = open('datos.csv','a') fdif.writelines(''+secuenciac+'; \n') fdif.close() Y esto eleva exponencialmente el tiempo. :( Estuve buscando en San Google y en un hilo anterior (así como en e foro de majibu) obtuve documentación donde se habla de lectura de archivos enormes con python usando un mínimo de tiempo: http://effbot.org/zone/wide-finder.htm def process(file, chunk): f = open(file) f.seek(chunk[0]) d = defaultdict(int) for page in pat.findall(f.read(chunk[1])): d[page] += 1 return Perdón pero no acabo de entender cómo implementar algo que me funcione utilizando el mínimo de tiempo como dicen que sucede por ejemplo con el process anterior. Se que se puede mejorar dichas situaciones pero lo poco que aún se de Python no me deja ser más eficiente (.. y es que mi ignorancia es infinita) Alguien me puede colaborar??? Muchas gracias. -- Manuel Enrique González Ramírez http://maengora.blogspot.com ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/
Re: [Python-es] Buscar, leer y escribir archivos grandes con Python
fc = open('archivo.txt','r') for linea in fc: b = linea.rfind('|') posi = b + 1 posf = posi + 9 secuenciac = linea[posi:posf] print secuenciac Has probado fc = open(.) for linea in fc: secuenciac = linea.split('|')[-1] ? ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/
Re: [Python-es] Buscar, leer y escribir archivos grandes con Python
Mejor aún, kiko: with open(archivo.txt, r) as entrada: with open(salida.txt, w) as salida: for linea in entrada: salida.write( + linea.split('|')[-1] + ;\n) Saludos El jue, 02-06-2011 a las 23:45 +0200, Kiko escribió: 2011/6/2 Jose Caballero jcaballero@gmail.com fc = open('archivo.txt','r') for linea in fc: b = linea.rfind('|') posi = b + 1 posf = posi + 9 secuenciac = linea[posi:posf] print secuenciac Has probado fc = open(.) for linea in fc: secuenciac = linea.split('|')[-1] Yo abro todos los días ficheros de ese pelo y no me tarda tanto. Prueba lo siguiente y dime a ver si te funciona: ficheroentrada = open('archivo.txt','r') ficherosalida = open('salida.txt','w') for linea in ficheroentrada: ficherosalida.write( + linea.split('|')[-1] + ;\n) ficheroentrada.close() ficherosalida.close() ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/ ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/
Re: [Python-es] Buscar, leer y escribir archivos grandes con Python
El día 2 de junio de 2011 23:32, Manuel Enrique González Ramírez maeng...@gmail.com escribió: Hola a tod@s, Tengo la siguiente situación: Un archivo de 29Mb que contiene 281.351 lineas. El archivo tiene la siguiente estructura: 1|2011-05-20 23:08:56|122711527|OPERADOR1|HOST TO HOST|212454|1|HOST CLIENTE|192630167 2|2011-05-20 23:09:08|122711530|OPERADOR12|HOST TO HOST|57212454|1000|HOST CLIENTE|192630168 3|2011-05-20 23:09:42|122711538|OPERADOR1|HOST TO HOST|454545454545|2000|HOST CLIENTE|192630169 4|2011-05-20 23:10:03|122711544|OPERADOR1|HOST TO HOST|121221211|2|HOST CLIENTE|192630170 5|2011-05-20 23:10:09|122711547|OPERADOR1|HOST TO HOST ESPECIAL|4545|5000|HOST CLIENTE|192630171 6|2011-05-20 23:10:46|122711554|OPERADOR1|HOST TO HOST ESPECIAL|545454445|5000|HOST CLIENTE|192630172 . . Del cual debo capturar la última sección, es decir, el número que me representa una secuencia (192630167, 192630168, etc). Lo hago así: fc = open('archivo.txt','r') for linea in fc: b = linea.rfind('|') posi = b + 1 posf = posi + 9 secuenciac = linea[posi:posf] print secuenciac Supongo que esto es muy lento y que con el split habrás notado una buena mejoría. Pero todavía se puede hacer mejor, rsplit es lo suyo: In [9]: linea = 1|2011-05-20 23:08:56|122711527|OPERADOR1|HOST TO HOST|212454|1|HOST CLIENTE|192630167 In [10]: timeit linea.split('|')[-1] 100 loops, best of 3: 1.2 us per loop In [11]: timeit linea.rsplit('|',1)[-1] 100 loops, best of 3: 695 ns per loop In [12]: linea.rsplit('|',1)[-1] Out[12]: '192630167' Para hacer esos mini-benchmarks tan chulos, se puede hacer facilmente con ipython. Para un profiling mejor busca por san google temas de python profiling e incluso python profiling kcachegrind. Lo suyo para optimizar es poder medir en que estas gastando el tiempo ;-) Esto tarda en promedio 3 minutos (que es demasiado comparado con el tiempo que tarda [1min en promedio] con el script que me pasaron en Perl). Ahora, para sumarle al problema del tiempo que tarda dicha proceso, el resultado del ciclo anterior debe escribirse en otro archivo (datos.csv); eso lo hago así: # Abro un archivo para edición fdif = open('datos.csv','a') fdif.writelines(''+secuenciac+'; \n') fdif.close() Y esto eleva exponencialmente el tiempo. :( No alcanzo a entender el uso de writelines: http://docs.python.org/library/stdtypes.html#file.writelines Supongo que no abres y cierras el fichero cada vez, pero tengo que preguntarlo, ¿lo haces? Si lo estás haciendo, un profiler gritaría que ese es el primer punto a optimizar. Estuve buscando en San Google y en un hilo anterior (así como en e foro de majibu) obtuve documentación donde se habla de lectura de archivos enormes con python usando un mínimo de tiempo: http://effbot.org/zone/wide-finder.htm def process(file, chunk): f = open(file) f.seek(chunk[0]) d = defaultdict(int) for page in pat.findall(f.read(chunk[1])): d[page] += 1 return Perdón pero no acabo de entender cómo implementar algo que me funcione utilizando el mínimo de tiempo como dicen que sucede por ejemplo con el process anterior. Se que se puede mejorar dichas situaciones pero lo poco que aún se de Python no me deja ser más eficiente (.. y es que mi ignorancia es infinita) Alguien me puede colaborar??? Creo que lo mejor es olvidarse un poco de conseguir la solución más eficiente por ahora. Primero juega con python y cuando tengas soltura irás haciendo el código más óptimo de forma fácil. Por ejemplo, la optimización del rsplit no se me hubiera podido ocurrir sin conocer la existencia del rsplit. Para saber por qué va más rápido hay que pensar en la cantidad de objetos a crear/destruir en una y otra solución, aparte de que una requiere un recorrido mayor de la cadena que la otra. Espero que el correo te haya resultado útil. Un saludo: Javi ___ Python-es mailing list Python-es@python.org http://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/