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 consciente del problema hasta que obtienes el resultado, el tiempo de ejecución del script que hace la tarea suele ser el menor de todos. Un programa en C que se ejecuta en 2 segundos y que tarda en escribirse 2 horas es peor solución a un programa ineficiente en python que tarda en escribirse 5 minutos y 10 en ejecutarse. En este caso concreto yo hubiera escrito lo siguiente: cat fichero | awk 'BEGIN{FS="|"}{print $9}' > resultado Simplemente porque un oneliner en la consola me cuesta menos que abrir el vim y escribir un script de python. 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/