Entrevista a Fernando Pérez, creador de IPython
Hoy os traemos una novedad desde Pybonacci: inauguramos la sección de entrevistas con Fernando Pérez, que ha accedido a charlar un rato con nosotros sobre el pasado, presente y futuro de IPython, la ciencia abierta y alguna que otra cosa más.
Enlaces
- Web de IPython http://ipython.org/
- Beca de la Fundación Sloan http://ipython.org/sloan-grant.html
- Etiqueta ipython en Stack Overflow http://stackoverflow.com/tags/ipython
- Lista de correo ipython-dev http://mail.scipy.org/mailman/listinfo/ipython-dev
- Información para desarrolladores https://github.com/ipython/ipython/wiki/Dev:-Index
- Chat de desarrolladores http://www.hipchat.com/ghtNzvmfC
Repaso a PyData 2013
Unos días después de la PyConUS 2013 se celebró la primera PyData del año (creo que serán semestrales de forma regular aunque el tiempo dirá). Entre las charlas había algunas introductorias, otras más avanzadas y otras enseñando aplicaciones prácticas.
Entre las charlas introductorias destacaremos:
Introducción a Numpy por Bryan Van De Ven: si no conoces absolutamente nada de Numpy esta es tu charla. Da un repaso por las cosas más frecuentes del uso de Numpy sin meterse en cosas muy esotéricas. Puedes sacar la libreta e ir apuntando las cosas que creas que te puedan resultar útiles para tus análisis.
Pandas por Wes McKinney: Es una charla introductoria. El problema que veo es que Pandas no es algo tan centrado como Numpy con su ndarray, las posibilidades de uso son múltiples y, quizá, hacer algo introductorio en vídeo sobre Pandas no resulte tan sencillo como hacerlo con Numpy. En general, la documentación de Pandas es aceptable (aunque incompleta en algunos momentos) y la veo como un buen punto de partida antes de empezar a ver vídeos sobre Pandas. Creo que lo mejor para empezar con Pandas es echarle un ojo al tour de 10 minutos en vídeo o en texto) y luego empezar a trastear con la librería y con la documentación para empezar a entenderlo. Por nuestra parte, estamos preparando nuestro tutorial cuyos primeros capítulos estaran disponibles en breve, stay tuned!!!! En esta conferencia ha habido más vídeos sobre Pandas pero son más avanzados (primero para marujear con datos de forma productiva, segundo (con numpy y statsmodels) para análisis de series temporales) .
Aprendiendo Python por Peter Norvig: Otro tutorial más para empezar con Python!!
Hacer bonitos gráficos con MatPlotLib por Mike Müller: Otro más avanzado muestra como hacer MatPlotLib más interactivo gracias al gran Jake Vanderplas. Os dejamos aquí nuestro tutorial de matplotlib por si alguno no lo conoce aún (#autobombo).
Visualización de datos con NodeBox por Lynn Cherny: Librería para hacer gráficos más ‘artísticos’. Yo tengo sentimientos encontrados con algunos enfoques de este tipo de gráficos (NodeBox, D3,…) por lo que te recomiendo mejor verlo y, si alguien quiere, lo discutimos en los comentarios.
Scikit-image por Davin Potts: Creo que esta librería es una de las grandes desconocidas y ofrece unas posibilidades muy interesantes. Si no la conoces deberías echarle un ojo al vídeo.
Entre las que hablan sobre cosas más prácticas y no específicamente de librerías destacaría (alguna no porque me haya gustado especialmente):
Análisis de redes sociales por Katherine Chuang: Estas están muy de moda (teoría de grafos) y están empezando a ser aburridas si no muestran algo excepcional o no sacan conclusiones **medibles** de todo el análisis chachiguay que hacen. Usa NetworkX, también muy de moda.
Plataforma de datos espacio temporales para el océano por André Karpistsenko: Esta me ha parecido interesante ya que muestra todo el pifostio de tecnologías y trabajo que hay detrás de muchas webs a las que voy a descargarme datos para mis análisis.
Hay más charlas avanzadas que hablan de HDF5 ([1]), Machine Learning ([1], [2], [3]), Blaze (el futuro de Numpy), IPython y más cosas del Big Data y herramientas Python para lidiar con ello.
Si le echáis un ojo a algún vídeo, por favor, dejad algún comentario más abajo para saber lo que os ha parecido.
Saludos y espero veros pronto entre esa gran cantidad de datos
Repaso a la PyConUS 2013
En nuestro repaso habitual a la parte científica de conferencias pythonistas importantes hoy le toca el turno a la última PyConUS celebrada en California la semana pasada:
Server log analysis with pandas (Taavi Burns): Ejemplos de uso de Pandas y las bondades de esta librería (a la que en breve le daremos un repaso importante). Si no conocéis Pandas es una buena forma de ver un poco tiempo muchas de sus ventajas. También se usa IPython y Matplotlib de forma general en la charla.
Building full-stack applications in Python (Luke Lee): En esta charla se echa un vistazo a todas las librerías ‘científicas’ masivas (Numpy and friends) que tenemos en Python. No aporta mucho si ya conoces un poco el ecosistema Numpy-científico. Como punto exótico, el uso de PyQwt en lugar de Matplotlib. Una librería gráfica más ligera que Matplotlib (y más fea, en mi humilde y subjetiva opinión).
Building an image processing pipeline with python (Franck Chastagnol): Un ejemplo de uso de Python en data-mining y en reconocimiento de caracteres con Numpy, openCV y Tesseract OCR (una librería en c++.
Crypto 101 (Laurens Van Houtven): Introducción a la criptografía, un poquito de matemáticas no hacen daño a nadie.
Cython Vs SWIG, Fight! (Mark Kohler): Acelerando (tus cálculos), que es gerundio.
Location, location, location (Julia Grace): Uso de bases de datos espaciales junto con GeoDjango. Ejemplos de uso y lecciones aprendidas.
MTO On Blast: Using Python’s Natural Language Toolkit to Model Gossip Blogs (Robert Elwell): Lingüística computacional y procesamiento de lenguaje natural usando la librería NLTK (algún día me gustaría tener tiempo para echarle un ojo a NLTK,…).
Realtime Tracking and Mapping of Geographic Objects using Python (Ragi Burhum): Si tienes algún interés en montar un servicio de mapas, geolocalización, etc, esta charla te puede guiar un poco. Pero no se habla gran cosa de GIS.
Teaching with the IPython notebook (Matt Davis): Nada que decir, mejor verlo si tienes que dar un tutorial de algo. Enseña el uso de ipythonblocks, herramienta inventada por el mismo Matt Davis.
Functional programming with Python (Mike Müller): Si te interesa la programación funcional y quieres hacerlo con python puedes echarle un ojo a este vídeo.
Awesome big data algorithms (Titus Brown): Un poco sobre el nuevo término de moda: Big data. Generamos información por encima de nuestras posibilidades.
Fighting cancer with Python (Erik Evensen): Un ejemplo genial del uso de python para resolver problemas médicos como el cáncer (con un pequeño homenaje a John Hunter, creador de Matplotlib, que nos dejó el pasado verano debido a un cáncer).
Music Theory and Performance Analysis with Sebastian and Czerny (James Tauber): No la he vista en detalle por lo que no la puedo comentar mucho.
SimpleCV – Computer vision using python (Katherine Scott): Un repaso a simpleCV si no lo conoces.
Visualizing Github, Part I: Data to information (Idan Gazit y Dana Bauer), Visualizing Github, Part I: information to mining (Idan Gazit y Dana Bauer): No las he pedido ver, las tengo como deberes de vuelta de vacaciones.
Advanced Machine learning with scikit-learn (Olivier Grisel): Aprendizaje automático con uno de los desarrolladores de la excepcional librería scikit-learn.
Analyzing social networks with python (Kouznetsov, Kazil y Tsvetovat): Data mining (para conocerte y venderte cosas que no necesites,…).
Como charlas curiosas y/o interesantes:
Using Python to Code by Voice (Tavis Rudd): Mejor verla, aviso que tiene algún momento surrealista
Python’s class development toolkit (Raymond Hettinger): Esta es una charla must-see.
Transfirmong code into beautiful idiomatin python (Raymond Hettinger): Otra charla del mismo tipo que no te puedes perder.
El resto de vídeos los tenéis en PyVideo, como siempre.
Saludos.
Revisión del tutorial de matplotlib.pyplot
El tutorial de matplotlib.pyplot publicado en este blog durante el 2012 ha sido revisado, corregido
y publicado con formato notebook de ipython.
Se ha subido el material al repositorio de pybonacci en github por lo que lo podéis descargar, usar, modificar, mejorar,…
Si lo queréis ver online podéis usar el nbviewer de la web de ipython.
P.D.: Nos estamos poniendo al día con lo último en boga de la comunidad científica. Y estad atentos puesto que el notebook de IPython va a seguir pegando fuerte.
Formateando números
Muy a menudo tengo que andar leyendo datos de sensores de medida conectados a ‘loggers’ con formatos muy variopintos y transformarlos a algo legible por un ser humano (echadle un ojo a regex mediante ejemplos para conocer el maravilloso mundo de las expresiones regulares y no ser tan bruto como yo a la hora de tratar esos variopintos formatos).
Hoy vamos a estar muy centrados en una sola cosa, vamos a ver como usar el método format de la clase string.
Vamos a empezar por muy algo sencillo, vamos a mostrar números en pantalla (o los podéis grabar en un fichero) de forma secuencial:
datos = range(1,200, 20) for dato in datos: print(dato)
La salida del anterior código mostrará:
1 21 41 61 81 101 121 141 161 181
Lo anterior está bien, pero no tenemos mucha libertad para poder alinear la salida como queramos. Por ello vamos a ver como podríamos representar lo anterior usando el método format. En una cadena o string podemos meter llaves (‘{}‘) y lo que vaya entre las llaves irá asociado a algún objeto que queremos formatear, lo que vaya entre las llaves se llama campo de reemplazo. En el siguiente ejemplo ponemos una llave y lo que se escriba dentro, en este caso no ponemos nada, irá asociado a la variable que coloquemos dentro del format.
for dato in datos: print('{}'.format(dato))
La salida del anterior código mostrará:
1 21 41 61 81 101 121 141 161 181
Ahora hemos conseguido lo mismo que al principio solo que ahora tenemos libertad para formatear mejor la salida. ¿Si tengo más de una variable como sabría qué variable hay que usar para cada par de llaves? Para resolver esto podemos usar números dentro de las llaves, lo que hemos llamado anteriormente campo de reemplazo, así el 0 corresponderá a la primera variable, el 1 corresponderá a la segunda,…
for i, dato in enumerate(datos): print('{0} {1}'.format(i, dato))
La salida del anterior código mostrará:
0 1 1 21 2 41 3 61 4 81 5 101 6 121 7 141 8 161 9 181
Lo anterior es equivalente a usar un nombres para las variables donde el nombre será el campo de reemplazo y será reemplazado por su valor, de esta forma no tenemos que conocer el orden en que se colocan las variables en el format como en el ejemplo anterior:
for i, dato in enumerate(datos): print('{contador} {valor}'.format(contador = i, valor = dato))
La salida del anterior código mostrará:
0 1 1 21 2 41 3 61 4 81 5 101 6 121 7 141 8 161 9 181
También podríamos usar un diccionario y acceder mediante sus claves:
for i, dato in enumerate(datos):
texto = {'contador' : i, 'valor' : dato}
print('{contador} {valor}'.format(**texto))
La salida del anterior código mostrará:
0 1 1 21 2 41 3 61 4 81 5 101 6 121 7 141 8 161 9 181
O no usar absolutamente nada, que no lo recomiendo a no ser que solo haya una cosa a sustituir dentro del string:
for i, dato in enumerate(datos): print('{} {}'.format(i, dato))
La salida del anterior código mostrará:
0 1 1 21 2 41 3 61 4 81 5 101 6 121 7 141 8 161 9 181
Pero todo lo anterior es muy feo, la segunda columna está alineada a la izquierda. ¿Cómo podemos hacer que se alinee a la derecha? Podemos usar los símbolos de alineación de los que disponemos:
‘<’ fuerza al campo a estar alineado a la izquierda dentro del espacio disponible y es el comportamiento por defecto
‘>’ fuerza al campo a estar alineado a la derecha y este sería el comportamiento por defecto para números pero, de momento, no usamos números, sino strings
‘=’ fuerza al ‘relleno’ a ser colocado después del signo (si lo hubiera) pero antes de los dígitos. Esto se usa para escribir campos de la forma ‘+000000120’ y solo válido para valores numéricos
‘^’ fuerza al campo a estar centrado dentro del espacio disponible.
Vamos a representar lo anterior para que veáis como funciona. La primera y última línea del código siguiente solo sirve para que tengáis una referencia. El 10 colocado dentro de las claves indica que vamos a usar 10 espacios para cada campo, más adelante veremos más sobre esto.
print('{0} {0} {0} {0}'.format('='*10))
for i, dato in enumerate(datos):
print('{0:<10} {1:>10} {2:=10} {3:^10}'.format(dato, dato, dato, dato))
print('{0} {0} {0} {0}'.format('='*10))
La salida del anterior código mostrará:
========== ========== ========== ========== 1 1 1 1 21 21 21 21 41 41 41 41 61 61 61 61 81 81 81 81 101 101 101 101 121 121 121 121 141 141 141 141 161 161 161 161 181 181 181 181 ========== ========== ========== ==========
Pero entre el segundo y el tercero no vemos diferencia,… Vamos a ver donde estaría la diferencia usando un símbolo ‘+’ para los números y así veremos donde se coloca en un caso y en el otro:
print('{0} {0} {0} {0}'.format('='*10))
for dato in datos:
print('{0:<10} {1:>+10} {2:=+10} {3:^10}'.format(dato, dato, dato, dato))
print('{0} {0} {0} {0}'.format('='*10))
La salida del anterior código mostrará:
========== ========== ========== ========== 1 +1 + 1 1 21 +21 + 21 21 41 +41 + 41 41 61 +61 + 61 61 81 +81 + 81 81 101 +101 + 101 101 121 +121 + 121 121 141 +141 + 141 141 161 +161 + 161 161 181 +181 + 181 181 ========== ========== ========== ==========
Esto ya empieza a ser un poco más excitante (que frizazo que soy). Si quisiéramos que en el espacio que dejamos a cada número no hubiera espacios vacios y se rellenaran con ’0′ haríamos lo siguiente:
print('{0} {0}'.format('='*10))
for i, dato in enumerate(datos): print('{0:^10} {1:>010}'.format(i, dato))
print('{0} {0}'.format('='*10))
La salida del anterior código mostrará:
========== ==========
0 0000000001
1 0000000021
2 0000000041
3 0000000061
4 0000000081
5 0000000101
6 0000000121
7 0000000141
8 0000000161
9 0000000181
========== ==========
¿Colocamos dos decimales en la segunda columna? Esto lo podemos conseguir usando los tipos de datos disponibles. En este caso vamos a usar ‘f’ para float colocándole dos puntos decimales dentro de los 10 espacios disponibles que definimos para la segunda columna:
print('{0} {0}'.format('='*10))
for i, dato in enumerate(datos): print('{0:^10} {1:>10.2f}'.format(i, dato))
print('{0} {0}'.format('='*10))
La salida del anterior código mostrará:
========== ==========
0 1.00
1 21.00
2 41.00
3 61.00
4 81.00
5 101.00
6 121.00
7 141.00
8 161.00
9 181.00
========== ==========
¿Y si usamos notación exponencial? Pues lo mismo lo que usando ahora el tipo ‘e’ o el tipo ‘E’.
print('{0} {0}'.format('='*10))
for i, dato in enumerate(datos): print('{0:^10} {1:>10.3e}'.format(i, dato))
print('{0} {0}'.format('='*10))
La salida del anterior código mostrará:
========== ==========
0 1.000e+00
1 2.100e+01
2 4.100e+01
3 6.100e+01
4 8.100e+01
5 1.010e+02
6 1.210e+02
7 1.410e+02
8 1.610e+02
9 1.810e+02
========== ==========
Si nuestros datos fueran valores en tanto por uno y los queremos transformar a valores en tanto por ciento (‘%’) podemos usar el tipo % solo válido para números.
print('{0} {1}'.format('='*10, '='*15))
for i, dato in enumerate(datos): print('{0:^10} {1:>15%}'.format(i, dato))
print('{0} {1}'.format('='*10, '='*15))
La salida del anterior código mostrará:
========== ===============
0 100.000000%
1 2100.000000%
2 4100.000000%
3 6100.000000%
4 8100.000000%
5 10100.000000%
6 12100.000000%
7 14100.000000%
8 16100.000000%
9 18100.000000%
========== ===============
Como resumen, entre las claves podéis poner el campo a reeemplazar, como se formateará el texto, que ancho (en caracteres) se usará el para el campo a reemplazar, qué tipo de dato se escribirá (decimal, entero, string, caracter,…).
Si queréis ampliar vuestros conocimientos en el uso del método format o en como formatear texto con python podéis echarle un ojo a:
This post has been published on wordpress.com from an ipython notebook using ipynb2wp
Integrar Fortran con Python usando F2PY
Introducción
En este artículo vamos a explicar cómo podemos utilizar bibliotecas escritas en Fortran (o FORTRAN) desde Python utilizando F2PY, cómo preparar el código Fortran para que este proceso sea lo más sencillo posible y cómo solventar los problemas que nos pueden surgir por el camino.
Hay dos casos en los que nos puede venir bien usar F2PY:
- Queremos escribir una parte de cálculo intensivo en Fortran para que sea eficiente, pero queremos controlar la lógica del programa desde Python.
- Tenemos código legado y el esfuerzo de portarlo a otro lenguaje es demasiado grande (código ilegible, ausencia de comentarios, fragilidad). Aún podemos aprovecharlo: con F2PY podemos llamarlo desde Python.
He utilizado numerosas referencias para escribir este artículo, y una fundamental es [ČERTÍK]. En esta web Ondrej Čertík, también un colaborador clave en proyectos como SymPy y NumPy, recoge una serie de buenas prácticas para escribir código Fortran moderno y portable, enlaza a los estándares del lenguaje, compara la sintaxis de Fortran y Python y detalla algunos métodos para integrar los dos. Por otro lado, [DOWLING] hace un desarrollo completo desde un programa en Python sin optimizar hasta un programa Fortran optimizado, y compara la velocidad de ejecución. Esta es otra referencia excelente, y merece la pena echarle un vistazo al resto de cursos de la Universidad de Cambridge.
Por supuesto, se puede consultar en Internet la referencia de F2PY original.
En esta entrada se han usado python 2.7.3, numpy 1.6.2 y gfortran 4.7.2.
Nota: Es posible que más adelante aparezca código todo en mayúsculas y sin sangrar. Quedáis avisados
Regex mediante ejemplos
Las expresiones regulares, regex o regexp siempre me han parecido algo especialmente críptico. La realidad es que nunca les dediqué un mínimo de tiempo ya que en el trabajo todo es para ayer y siempre acabo acudiendo a soluciones ‘stackoverfloweras’ donde los super expertos siempre están ahí (gracias chicos). Pero se acabó, aprovechando que estoy en un avión y tengo unas horas y que me he planificado y descargado varios recursos previamente para poder trabajar ‘offline’ voy a aprovechar para intentar que lo aprendamos o, al menos, nos introduzcamos en ello mediante ejemplos y, así, nos acerquemos más a ser unos expertos mineros de datos y podamos extraer la correcta información a analizar.
Aunque en python se puedan buscar patrones de otras formas diferentes vamos a usar el módulo re de la librería estándar.
import re
Vamos al lío. Imaginad que tenéis una cadena de e-mails en texto plano formateados de la forma que figura a continuación.
texto = """ De: monete_que_no_ve@lostresmonetes.net Enviado el: Jueves, 18 de noviembre de 2012 a las 13:22 Para: torpedo@submarino.com Asunto: Re: Conquistar el mundo Hola. Aapfojewagf ajwa wjepofoisa jvgoisajigf jewapoijewagomsod moisjaoigjpoewijsn dsanigeaoi. Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. De: torpedo@submarino.com Enviado el: Jueves, 18 de noviembre de 2012 a las 12:42 Para: monete_que_no_ve@lostresmonetes.net Asunto: Re: Conquistar el mundo Hola. Aapfojewagf ajwa wjepofoisa jvgoisajigf jewapoijewagomsod moisjaoigjpoewijsn dsanigeaoi. Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. De: monete_que_no_ve@lostresmonetes.net Enviado el: Jueves, 18 de noviembre de 2012 a las 11:57 Para: torpedo@submarino.com Asunto: Re: Conquistar el mundo Hola. Aapfojewagf ajwa wjepofoisa jvgoisajigf jewapoijewagomsod moisjaoigjpoewijsn dsanigeaoi. Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. De: torpedo@submarino.com Enviado el: Jueves, 18 de noviembre de 2012 a las 11:54 Para: monete_que_no_ve@lostresmonetes.net Asunto: Re: Conquistar el mundo Hola. Aapfojewagf ajwa wjepofoisa jvgoisajigf jewapoijewagomsod moisjaoigjpoewijsn dsanigeaoi. Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. De: monete_que_no_ve@lostresmonetes.net Enviado el: Jueves, 18 de noviembre de 2012 a las 09:15 Para: torpedo@submarino.com Asunto: Conquistar el mundo Hola. Aapfojew mi primo el monete que no habla (Monete.que.no.habla@lostresmonetes.net) agf ajwa wjepofoisa jvgoisajigf. Para la ninia + w@pa del tuenti, hoygan (Monete_que_no_escucha@lostresmonetes.co.uk). Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. """
Imaginad que nuestro problema es que queremos saber cuantas direcciones de correo diferentes aparecen en toda la cadena de correos anterior (almacenada en la variable ‘texto’. Para la cadena anterior sería sencillo hacerlo a mano, pero imaginad que hay miles de correos.
Una forma sería usar el siguiente patrón: '\S+@\S+'
donde
'\S' encuentra cualquier caracter que no sea un espacio en blanco (en las direcciones de correo no está permitido usar espacios en blanco). Sería equivalente a usar r’[^ \t\n\r\f\v]‘ (mirad aquí para ver qué es esto último entre corchetes)
'+' indica que hay que encontrar al menos un caracter que no sea un espacio en blanco
'@' indica la arroba
No voy a hablar de ninguna de las funciones del módulo re ya que para eso tenéis la documentación oficial de python. Empezaré usando la función findall para los primeros ejemplos.
print(re.findall('\S+@\S+', texto))
La salida del anterior código mostrará:
['monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'torpedo@submarino.com', 'monete_que_no_ve@lostresmonetes.net', 'monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'torpedo@submarino.com', 'monete_que_no_ve@lostresmonetes.net', 'monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', '(Monete.que.no.habla@lostresmonetes.net)', 'w@pa', '(Monete_que_no_escucha@lostresmonetes.co.uk).']
Vaya, entre los resultados se nos han colado cosas que no serían direcciones de correo (los tres últimos elementos de la lista). Vamos a intentar solucionarlo usando un patrón un poco más complejo.
El patrón propuesto ahora sería algo como lo siguiente: '\w+@\w+'
donde
'\w' encuentra cualquier caracter que sea alfanumérico (todas las letras mayúsculas y minúsculas, los números y el símbolo '_'). Esto sería equivalente a usar r'[a-zA-Z0-9_]'
'+' indica que hay que encontrar al menos un caracter que no sea un espacio en blanco
'@' indica la arroba
print(re.findall('\w+@\w+',texto))
La salida del anterior código mostrará:
['monete_que_no_ve@lostresmonetes', 'torpedo@submarino', 'torpedo@submarino', 'monete_que_no_ve@lostresmonetes', 'monete_que_no_ve@lostresmonetes', 'torpedo@submarino', 'torpedo@submarino', 'monete_que_no_ve@lostresmonetes', 'monete_que_no_ve@lostresmonetes', 'torpedo@submarino', 'habla@lostresmonetes', 'w@pa', 'Monete_que_no_escucha@lostresmonetes']
Ups, vaya, como hemos usado ‘\w’ se han perdido las terminaciones de las direcciones de correo a continuación del símbolo ‘.’ (‘.net’, ‘.com’, ‘.co.uk’) ya que no está incluido en la búsqueda. También vemos que hemos extraído incorrectamente una de las direcciones de correo que usa ‘.’ antes de la ‘@’ (‘Monete.que.no.habla@lostresmonetes.net’)
Vamos a volver a probar con un patrón diferente: r'\w+[\.]*@\w+[\.]*\w+'
donde:
'\w' encuentra cualquier caracter que sea alfanumérico (todas las letras mayúsculas y minúsculas, los números y el símbolo '_'). Esto sería equivalente a usar [a-zA-Z0-9]
'[\.]' incluye el símbolo '.' dentro del patrón a buscar. Sería equivalente a usar r'[a-zA-Z0-9_\.]'
'+' indica que hay que encontrar al menos un caracter que no sea un espacio en blanco
'@' indica la arroba
print(re.findall(r'\w+[\.]*@\w+[\.]*\w+', texto))
La salida del anterior código mostrará:
['monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'torpedo@submarino.com', 'monete_que_no_ve@lostresmonetes.net', 'monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'torpedo@submarino.com', 'monete_que_no_ve@lostresmonetes.net', 'monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'habla@lostresmonetes.net', 'w@pa', 'Monete_que_no_escucha@lostresmonetes.co']
Vaya. Hemos recuperados las terminaciones del host (‘.net’, ‘.com’) pero no hemos recuperado correctamente la dirección de correo errónea ni la dirección del correo con terminación ‘.co.uk’… ¿Qué podemos hacer? Pues probar con otro patrón que haga lo que necesitamos.
El patrón propuesto ahora sería: r'[\w\.]*@[\w\.]*'
donde
‘[\w\.]*’ busca cualquier cosa que contenga una letra (desde la a la z en mayúsculas o minúsculas), un número, el símbolo '_' y/o el símbolo '.'
'@' indica la arroba
print(re.findall(r'[\w\.]*@[\w\.]*', texto))
La salida del anterior código mostrará:
['monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'torpedo@submarino.com', 'monete_que_no_ve@lostresmonetes.net', 'monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'torpedo@submarino.com', 'monete_que_no_ve@lostresmonetes.net', 'monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'Monete.que.no.habla@lostresmonetes.net', 'w@pa', 'Monete_que_no_escucha@lostresmonetes.co.uk']
Maldición, se nos ha seguido colando una cosa que no es una dirección de correo. Podría eliminarla pidiéndo que después de la arroba deba figurar al menos un símbolo '.'.
Nuevo patrón: r'[\w\.]*@\w*\.[\w\.]*'
donde
'[\w\.]*' busca cualquier cosa que contenga una letra (desde la a la z en mayúsculas o minúsculas), un número, el símbolo '_' y/o el símbolo '.'
'@' indica la arroba
'\w+\.[\w\.]*' primero busca cualquier cosa que contenga al menos una letra (desde la a la z en mayúsculas o minúsculas), un número y/o el símbolo '_', segundo, exige que haya un punto y, por último, vuelve a buscar cualquier cosa que contenga una letra (desde la a la z en mayúsculas o minúsculas), un número, el símbolo '_' y/o el símbolo '.'. Es decir, este último subpatrón encontraría cosas como por ejemplo ‘hola.com’, ‘hola.co.uk’, ‘hola_.com’, ‘hola.co_m’,…, que no tienen que ser correctas como dominio o ‘host’ pero que permiten filtrar a ‘w@pa’
print(re.findall(r'[\w\.]*@\w+\.[\w\.]*', texto))
La salida del anterior código mostrará:
['monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'torpedo@submarino.com', 'monete_que_no_ve@lostresmonetes.net', 'monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'torpedo@submarino.com', 'monete_que_no_ve@lostresmonetes.net', 'monete_que_no_ve@lostresmonetes.net', 'torpedo@submarino.com', 'Monete.que.no.habla@lostresmonetes.net', 'Monete_que_no_escucha@lostresmonetes.co.uk']
Qué pasa si, por la razón que sea, queremos obtener el usuario del correo y el dominio por separado para ¡lo que sea que se te ocurra! Ha llegado el momento de introducir los grupos. Los grupos son patrones o subpatrones encerrados entre paréntesis.
Podemos proponer el siguiente patrón y ver qué pasa: r'([\w\.]*)@(\w+\.[\w\.]*)'
Este patrón es el mismo que el de antes pero encerrando lo que queremos que sea un grupo entre paréntesis.
Para el siguiente ejemplo vamos a usar la función finditer en lugar de la función findall, ambas del módulo re.
iterador = re.finditer(r'([\w\.]*)@(\w+\.[\w\.]*)', texto)
users = []
hosts = []
for tupla in iterador:
users.append(tupla.group(1))
hosts.append(tupla.group(2))
print('Nos han escrito:')
for user in set(users): print(' ' + user)
print('desde los siguientes dominios')
for host in set(hosts): print(' ' + host)
La salida del anterior código mostrará:
Nos han escrito: torpedo Monete.que.no.habla Monete_que_no_escucha monete_que_no_ve desde los siguientes dominios submarino.com lostresmonetes.net lostresmonetes.co.uk
El iterador devuelve un objeto Match, que es una clase con sus métodos y sus cosicas. El método group nos devolverá el elemento del grupo que le pidamos. En este caso se usaría el índice 1 para el primer grupo, 2 para el segundo y 0 o nada para que nos devuelva todo lo encontrado con el patrón usado, es decir, lo mismo que si no hubiéramos usado grupos.
En este caso solo se usan dos grupos en el patrón pero podría darse el caso de que el patrón se volviese más complejo y nos interesase incluir más grupos. Para evitar liarnos podríamos usar nombres para los grupos de la siguiente manera:
Patrón: r'(?P<users>[\w\.]*)@(?P<hosts>\w+\.[\w\.]*)'
donde
'?P<nombre_del_grupo>' es la forma de identificar el grupo con nombre_del_grupo siendo el valor que quieras usar para nombrar a ese determinado grupo.
En la pieza de código siguiente deberéis reemplazar en la primera línea patron por r'(?P<users>[\w\.]*)@(?P<hosts>\w+\.[\w\.]*)'. Disculpad las molestias pero wordpress.com ‘escapa’ algunas cosas del código.
iterador = re.finditer(patron, texto)
hosts = []
users = []
for tupla in iterador:
users.append(tupla.group('users'))
hosts.append(tupla.group('hosts'))
print('Nos han escrito:')
for user in set(users): print(' ' + user)
print('desde los siguientes dominios')
for host in set(hosts): print(' ' + host)
La salida del anterior código mostrará:
Nos han escrito: torpedo Monete.que.no.habla Monete_que_no_escucha monete_que_no_ve desde los siguientes dominios submarino.com lostresmonetes.net lostresmonetes.co.uk
Ahora queremos sustituir la dirección del usuario por otro patrón para así ocultar sus direcciones. Eso lo podemos hacer mediante las funciones sub o subn de la siguiente forma.
En la pieza de código siguiente deberéis reemplazar en la primera línea patron por r'(?P<users>[\w\.]*)@(?P<hosts>\w+\.[\w\.]*)' y patron2 por r'----------@\g<hosts>'. Disculpad las molestias pero wordpress.com ‘escapa’ algunas cosas del código.
print(re.sub(patron, patron2, texto))
La salida del anterior código mostrará:
De: ----------@lostresmonetes.net Enviado el: Jueves, 18 de noviembre de 2012 a las 13:22 Para: ----------@submarino.com Asunto: Re: Conquistar el mundo Hola. Aapfojewagf ajwa wjepofoisa jvgoisajigf jewapoijewagomsod moisjaoigjpoewijsn dsanigeaoi. Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. De: ----------@submarino.com Enviado el: Jueves, 18 de noviembre de 2012 a las 12:42 Para: ----------@lostresmonetes.net Asunto: Re: Conquistar el mundo Hola. Aapfojewagf ajwa wjepofoisa jvgoisajigf jewapoijewagomsod moisjaoigjpoewijsn dsanigeaoi. Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. De: ----------@lostresmonetes.net Enviado el: Jueves, 18 de noviembre de 2012 a las 11:57 Para: ----------@submarino.com Asunto: Re: Conquistar el mundo Hola. Aapfojewagf ajwa wjepofoisa jvgoisajigf jewapoijewagomsod moisjaoigjpoewijsn dsanigeaoi. Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. De: ----------@submarino.com Enviado el: Jueves, 18 de noviembre de 2012 a las 11:54 Para: ----------@lostresmonetes.net Asunto: Re: Conquistar el mundo Hola. Aapfojewagf ajwa wjepofoisa jvgoisajigf jewapoijewagomsod moisjaoigjpoewijsn dsanigeaoi. Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl. De: ----------@lostresmonetes.net Enviado el: Jueves, 18 de noviembre de 2012 a las 09:15 Para: ----------@submarino.com Asunto: Conquistar el mundo Hola. Aapfojew mi primo el monete que no habla (----------@lostresmonetes.net) agf ajwa wjepofoisa jvgoisajigf. Para la ninia + w@pa del tuenti, hoygan (----------@lostresmonetes.co.uk). Ajfpoijwafe sodvm osznfinewahaw eoansjgndsakjnglkjds. Alkjndszkng aigpiewannalkjndkjnlkjdznvns ln sa nfpoiewa npoinpewnpofn. N<sznvcknvknkxzvnoisajpoijewaoi jmsam lkvznapiunea engnal nfsl.
Y, de momento, ya vale. Solo hemos rascado un poco pero espero que os haya valido de algo. Si encuentro tiempo habrá un capítulo II con más ejemplos para que este tutorial o lo que sea que haya salido se amplíe con cosas más complejas (a medida que tenga más soltura con ello).
Si queréis seguir por vuestra cuenta podéis usar:
La documentación oficial del módulo re
El HOW-TO de la documentación oficial
Expresiones regulares en Google-developers
La aplicación de escritorio KODOS o una versión online de la misma herramienta.
Algún completo libro sobre expresiones regulares
Saludos.
P.D.: Como siempre, se aceptan todo tipo de críticas constructivas y se agradecen todo tipo de correcciones a cosas incorrectas que haya dicho.

