Re: [Python-es] Buscar, leer y escribir archivos grandes con Python

2011-06-03 Por tema lasizoillo
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

2011-06-03 Por tema Kiko
 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

2011-06-03 Por tema Alexis Roda

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] Mapear un 'get_attr' [P]

2011-06-03 Por tema Edinson Padron

Saludos a todos,

silly question... no logro recordar una función y mucho menos el modulo 
que la contiene, posiblemente la reconozcan si les describo la situación 
para la cual fue desarrollada:


map(lambda x: x.att, iterable)

obviamente dicha función cumple el objetivo de esta porción del código 
'lambda x: x.att'


Muchas gracias por su tiempo.
___
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] Mapear un 'get_attr' [P]

2011-06-03 Por tema Carlos Zuniga
2011/6/3 Edinson Padron edinson.padro...@gmail.com:
 Saludos a todos,

 silly question... no logro recordar una función y mucho menos el modulo que
 la contiene, posiblemente la reconozcan si les describo la situación para la
 cual fue desarrollada:

 map(lambda x: x.att, iterable)

 obviamente dicha función cumple el objetivo de esta porción del código
 'lambda x: x.att'


te refieres a getattr?

getattr(x, 'att')

por otro lado, tambien podrias usar esto:

[x.att for x in iterable]

Saludos
-- 
Linux Registered User # 386081
A menudo unas pocas horas de Prueba y error podrán ahorrarte minutos
de leer manuales.
___
Python-es mailing list
Python-es@python.org
http://mail.python.org/mailman/listinfo/python-es
FAQ: http://python-es-faq.wikidot.com/