Professional Documents
Culture Documents
Publicado el 21/02/2011 por Luis Jos En esta entrega de la serie de Archivos, abordaremos algunas estrategias para procesar los datos de archivos obtenidos en la red, archivos de hojas de calculo y otros de contenido especial, como parejas de nmeros reales. El temario de procesamiento de datos contenido de archivos es bastante amplio.
Una corrida del programa produce la salida siguiente: [(-1.0, 2.0), (0.0, -0.01), (0.0, 1.0), (1.0, 0.0), (1.0, 1.0), (1.3, 0.0), (2.5, -2.5), (3.0, -1.5), (10.5, -1.0)] Se hace resaltar que esta solucin de la tarea descansa fuertemente en el hecho de que no se admiten espacios blancos dentro de los pares ordenados, pues, de lo contrario, la aplicacin del mtodo split() para obtener las parejas en la lnea no funcionara. Para solventar este problema, podemos eliminar todos los blancos en la lnea y se nos presentaran dos alternativas para la solucin. La primera consiste en observar que los pares estn separados por el texto )(, con lo que podemos separarlos con respecto a ste; pero el primer y ltimo par de cada lnea todava presentarn un parntesis que se debe remover. El cdigo Python parejas2.py que implementa esta solucin se muestra en el listado 3 Listado 3. Cdigo de parejas2.py
# -*- coding: utf-8 -*def listar_pares(): entrada = open('pares2.dat') lineas = entrada.readlines() lista = [] # lista de (n1, n2) pares de nmeros for linea in lineas: linea = linea.strip() # remover todos los blancos linea = linea.replace(' ', '') # crear una lista de parejas sin parntesis final e inicial parejas = linea.split(')(') # quitar los parntesis inicial/final en parejas primera/ltima: parejas[0] = parejas[0][1:] # (-1,3 -> -1,3 parejas[-1] = parejas[-1][:-1] # 8.5,9) -> 8.5,9 for pareja in parejas: n1, n2 = pareja.split(',') n1, n2 = float(n1), float(n2) par = (n1, n2) lista.append(par) entrada.close() return lista def principal(): pares = listar_pares() pares = sorted(pares) print pares if __name__ == '__main__': principal()
La segunda alternativa consiste en remover todos los blancos, eliminar los parntesis iniciales y y reemplazar los parntesis finales con blancos para separar lo pares. El resto de cdigo permanece igual. La solucin est implementada en parejas3.py Listado 4. Cdigo de parejas3.py
# -*- coding: utf-8 -*def listar_pares(): entrada = open('pares2.dat') lineas = entrada.readlines() lista = [] # lista de (n1, n2) pares de nmeros for linea in lineas: # remover espacios blancos, como nueva lnea linea = linea.strip() # quitar los blancos dentro de los parntesis linea = linea.replace(' ', '') # remover los parntesis iniciales linea = linea.replace('(', '') # quitar parntesis finales agregando un blanco de separacin linea = linea.replace(')', ' ') parejas = linea.split() for pareja in parejas: n1, n2 = pareja.split(',') n1, n2 = float(n1), float(n2) par = (n1, n2) lista.append(par) entrada.close() return lista def principal(): pares = listar_pares() pares = sorted(pares) print pares if __name__ == '__main__': principal()
Los programas parejas2.py y parejas3.py se pueden probar con el archivo pares2.dat cuyo contenido muestro en el Listado 5. Se puede observar los espacios blancos dentro de los parntesis. Listado 5. Contenido de pares2.dat (1.3 , 0 ) (-1 , 2) ( 3 , -1.5) ( 0.0 , 1) ( 1, 0 ) ( 1 , 1 ) (0,-0.01) ( 10.5,-1 ) ( 2.5, -2.5)
Una de las aplicaciones de usuario final ms populares es la aplicacin de hoja de clculo y su uso para desarrollar clculos matemticos y realizacin de grficos ha crecido consistentemente. En paralelo, Python puede ser utilizado para realizar computaciones como de hoja de clculo, aunque ventajosamente puede extender stos ms all de lo que puede hacer una hoja de clculo. Es posible combinar un programa de hoja de clculo con nuestros propios programas Python y lograr beneficios adicionales. La combinacin de estos dos softwares puede hacerse por intermedio de un archivo de formato CSV (Comma Separated Values -> Valores Separados por Coma). Este formato es muy simple: cada fila de la tabla de la hoja de clculo es una lnea en el archivo CSV, y los valores cada fila se encuentran separados por una coma u otro carcter de separacin que se elija (;, t, { blanco }) y los campos de texto encerrados por comillas dobles o simples. El proceso consta de tres pasos: 1. Guardar la tabla de la hoja de clculo en un archivo CSV, 2. Procesar el archivo CSV con un programa Python y guardar la salida en un nuevo archivo CSV, y 3. Leer este segundo archivo con el programa de hoja de clculo para su procesamiento ulterior. Procedamos con un ejemplo. La imagen siguiente presenta una sencilla hoja de clculo de OpenOffice, . La tabla contiene 4 x 4 celdas, donde la primera fila contiene cabeceras de columnas y la primera columna contiene cabeceras de fila. La restante subtabla 3 x 3 contiene nmeros a los cuales se les ha sustituido la coma decimal por un punto para facilitar las tareas y poder realizar los clculos en Python.
Vamos a guardar esta tabla en el archivo tabla1.cvs en formato CVS, quedando el archivo como sigue: Listado 6. Contenido de tabla1.cvs
"","Ao 1","Ao 2","Ao 3","Ao 4" "Persona 1","12400.34","12834.86","45322.65","45673.79" "Persona 2","43564.98","86754.67","65348.25","23574.34" "Persona 3","23574.34","14357.70","83476.45","45322.65" "Persona 4","98887.34","35675.98","16478.65","86754.67" "Persona 5","34276.98","76589.50","15347.75","12834.86"
La tarea consistir en escribir el cdigo necesario para cargar los datos de la hoja en una tabla, que ser una lista de listas donde cada elemento de la lista es una fila de la tabla y cada fila es una lista de los valores en las columnas de la tabla. Los archivos CSV pueden ser ledos lnea por lnea directamente o con ayuda del mdulo csv de la librera estndar de Python; escogeremos esto ltimo. El cdigo sera el siguiente: Listado 7. Cdigo de lectura del archivo CSV
import csv entrada = open('tabla1OO.csv') tabla = [] for fila in csv.reader(entrada): tabla.append(fila) entrada.close()
La variable fila es una lista de valores de campos ledos de entrada por el mdulo csv. Las tres lneas de este procesamiento se pueden resumir en
tabla = [fila for fila in csv.reader(entrada)]
Agregumosle al cdigo anterior las siguientes lneas y ejecutmoslo. La salida muestra como ha quedado representada ahora la hoja de clculo en Python:
import pprint pprint.pprint(tabla)
La salida es:
[["", "Ao 1", "Ao 2", "Ao 3", "Ao ["Persona 1", "12400.34", "12834.86", ["Persona 2", "43564.98", "56754.67", ["Persona 3", "23574.34", "34357.70", ["Persona 4", "98887.34", "75675.98", ["Persona 5", "34276.98", "76589.50", 4"], "45322.65", "65346.25", "83476.45", "56478.65", "55347.75", "55673.79"], "33574.34"], "45322.65"], "46754.67"], "62834.86"]]}
Es una lista de listas, donde cada entrada est encerrada entre comillas dobles, lo que indica que son objetos cadena. Para poder hacer clculos numricos se necesita convertir las
cadenas numricas a objetos float. Esta transformacin debe aplicarse, entonces, a partir de la fila 1 hasta len(tabla) y de la la columna 1 hasta len(tabla[0]-1). El cdigo para la conversin numrica es, entonces: Listado 8. Cdigo para conversin numrica
for fila in range(1, len(tabla)): for col in range(1, len(tabla[0]-1): tabla[fila][col] = float(tabla[fila][col]
Las entradas procesadas presentan ahora un formato numrico float. Procesamiento de los datos. Vamos a hacer unos clculos muy sencillos, digamos agregar una nueva fila con la suma de los valores de la columna respectiva.
# Inicializar una lista nula lista = [0.0]*len(tabla[0]) fila[0] = 'Suma' for c in range(1, len(fila)): suma = 0 for f in range(1, len(tabla)): suma += tabla[f]1 fila1 = suma tabla.append(fila)
La tabla ahora representa una hoja de clculo de 7 filas y 5 columnas como puede constatarse imprimindola. [['', 'Ao 1', 'Ao 2', 'Ao 3', 'Ao 4'] ['Persona 1', 12400.34, 12834.86, 45322.65, 55673.79] ['Persona 2', 43564.98, 56754.67, 65348.25, 33574.34] ['Persona 3', 23574.34, 34357.67, 83476.45, 45322.65] ['Persona 4', 98887.34, 75675.98, 56478.65, 46754.67] ['Persona 5', 34276.98, 76589.50, 55347.75, 62834.86] ['Suma', 212703.98, 256212.71, 305973.75, 244160.31]] Escritura de Archivos CSV. El siguiente paso del proceso consiste en escribir la tabla modificada en un archivo .csv de modo que pueda ser leda por un programa de hoja de clculo. Esto se logra con el cdigo siguiente;
salida = open('tablaHojaCalc2.csv', 'w')
Cargar el archivo en una hoja de clculo. El ltimo paso consiste en leer el archivo en una hoja de clculo, para lo cual debemos indicarle al programa, en el dilogo abrir que los datos de la hoja estn separados por comas. La imagen siguiente muestra nuestra tabla.
Figura 2. Archivo CSV cargado en OOCalc El cdigo completo del programa se lista a continuacin. Listado 9. Cdigo de procesarHojaCalc.py
# -*-coding: utf-8 -*import csv # Lectura del archivo entrada = open('tablaHojaCalc1.csv') tabla = [] tabla = [fila for fila in csv.reader(entrada)] for f in range(1, len(tabla)): for c in range(1, len(tabla[0])): tabla[f]1 = float(tabla[f]1) # Procesamiento de los datos fila = [0.0] * len(tabla[0]) fila[0] = 'Suma' for c in range(1, len(fila)): suma = 0 for f in range(1, len(tabla)): suma += tabla[f]1
fila1 = suma tabla.append(fila) # Escritura del archivo CSV salida = open('tablaHojaCalc2.csv', 'w') writer = csv.writer(salida) for fila in tabla: writer.writerow(fila) salida.close()
La tabla de datos procesada y cargada en Calc, luego de reemplazar los puntos decimales por comas, se muestra en la Figura 3.
Figura 3. Tabla modificada cargada en Calc Por qu usar el mdulo csv de Python? En el presente caso, separar los datos de las celdas mediante fila = fila.split(,) funciona perfectamente, pero no se comportara bien si en alguna celda hubiera un dato con texto conteniendo una coma; en tal caso, .split(,) separara esta celda rompiendo la estructura de la tabla, lo cual es evitado por el mdulo csv.
etiquetas HTML. Pero en ocasiones, la situacin puede simplificarse, pues los datos a menudo aparecen en archivos de texto que pueden ser accedidos mediante una url y visualizados en un explorador web. El mdulo urlib contiene las funciones urlopen() que abre una pgina web para lectura y retorna un objeto parecido a un archivo que uno puede manipular como si fuera un archivo local; y la funcin urlretrieve() con la cual podemos descargarlo a nuestra mquina. Consideremos los datos contenidos en el archivo remoto: http://robjhyndman.com/tsdldata/roberts/edgeman.dat Si accedemos a este archivo egman.dat, el explorador web muestra lo siguiente (traduccin ma, mis disculpas por cualquier error): Listado 10. Contenido del archivo edegman.dat
Mediciones de espesor central y diferencias axiales #Mediciones de espesor central (en mils) y diferencia axial (en mils) de #25 lentes de contacto extrados del proceso de produccin a intervalos regulares. #Tolerancias: 0.4 mil +/- 0.01 mil para el espesor central; la diferencia axial debe #ser menor que 0.0025 mil. #Tomado de Rick L. Edgeman and Susan B. Athey, "Digidot Plots for Process #Surveillance", Quality Progress, Mayo, 1990, 66-68. .3978 .4019 .4031 .4044 .3984 .3972 .3981 .3947 .4012 .4043 .4051 .4016 .3994 .3999 .4062 .4048 .4071 .4015 .3991 .4021 .4009 .3988 .3994 .4016 .4010 .0009 .0007 .0012
.0011 .0015 .0018 .0017 .0022 .0014 .0018 .0010 .0006 .0007 .0011 .0014 .0012 .0011 .0015 .0010 .0009 .0013 .0014 .0016 .0011 .0019
Las primeras 7 lneas estn compuestas por una cabecera y 6 lneas de comentarios. Los datos de las lneas 8 a la 32 son medidas de los espesores del centro, y las 33 y siguientes son diferencias axiales. La tarea a realizar es:
1. Crear un archivo 'medidas.dat' con la cabecera Espesor Error esp Dif Axial Obs donde Error esp es la diferencia absoluta entre el espesor y 4.0 y Obs, un texto indicando si la diferencia axial es mayor que 0.0025 2. Calcular el promedio de los espesores, el promedio de los errores de espesor, el promedio de las diferencias axiales y la desviacin estndar de estas medidas. 3. Imprimir estos promedios y dispersiones al pie del archivo.
Los anteriores pasos son fcilmente integrables en una funcin parincipal() y desarrollados en funciones auxiliares. He aqu nuestra primera aproximacin: Listado 11. Funcin principal()
#! /usr/bin/python # -*- coding: utf-8 -*-
import urllib def principal(): # Leer el archivo web para obtener los datos url = 'http://robjhyndman.com/tsdldata/roberts/edgeman.dat' entrada = urllib.urlopen(url) datos = obtener_datos(entrada) # Escribir los datos escribir_datos_archivo(datos) # Procesar los datos y obtener los resultados resultados = procesar_datos(datos) # Agregar los resultados al archivo de salida escribir_resultados(resultados) if __name == "__main__": principal()
En la lnea 8 asignamos la direccin web del archivo requerido a la variable url, que se pasa como parmetro al mtodo urlopen(). Este devuelve un objeto parecido a un archivo que se asigna a la variable entrada. Ahora, desarrollemos las funciones auxiliares.
Saltar Cabecera y Comentarios. El archivo ledo tiene 7 lneas (cabecera ms comentarios) que debemos saltar por lo que las leemos sin guardarlas. Esto posiciona el punto de lectura en la primera lnea de datos.
Lectura de datos. Las siguientes 25 lneas corresponden a las medidas de espesor central. Leeremos estos datos mediante un bucle for; este bucle se aprovecha para calcular la diferencia absoluta entre 0.4 (ver tolerancias) y el espesor medido. Los valores respectivos se guardan en las sub listas datos[0] (espesores) y datos[1] (errores de espesor). Terminado el bucle anterior, el punto de lectura en el archivo se posiciona al inicio de las lneas de valores de diferencias axiales; stas se leen mediante el segundo ciclo for y los valores se guardan en la sub lista d[2] de dif_axiales.
errores = d[1] diferencias = d[2] estadisticos = [[], []] # todas las listas tienen el mismo tamao n = len(d[0]) # Calcular la media de las medidas media_espesores = sum(d[0]) / n media_errores = sum(d[1]) / n media_diferencias = sum(d[2]) / n estadisticos[0].append(media_espesores) estadisticos[0].append(media_errores) estadisticos[0].append(media_diferencias) # Clculo de las desviaciones tpicas # desviacin tpica de los espesores dt_esp = calc_dt(espesores, media_espesores) # desviacin tpica de los errores dt_err = calc_dt(errores, media_errores) # desviacin tpica de las diferencias axiales dt_dif = calc_dt(diferencias, media_diferencias) estadisticos[1].append(dt_esp) estadisticos[1].append(dt_err) estadisticos[1].append(dt_dif) return estadisticos
La funcion comienza con la necesaria importacin de la funcin sqrt() del mdulo math, para poder calcular las desviaciones estndar de las medidas. Se evita la repeticin de la rutina de clculo de la desviacin incorporando la funcin auxiliar calc_dt() dentro del cuerpo de procesar_datos().
s.write('\n\n') s.write('{}{:<17}{:<13.4}{:<15.4}{:<10.4}'.format(' '*3, 'Desv Tipicas', res[1][0], res[1][1], res[1][2])) s.write(linea2) return
errores = d[1] diferencias = d[2] estadisticos = [[], []] # todas las listas tienen el mismo tamao n = len(d[0]) # Calcular la media de las medidas media_espesores = sum(espesores) / n media_errores = sum(errores) / n media_diferencias = sum(diferencias]) / n estadisticos[0].append(media_espesores) estadisticos[0].append(media_errores) estadisticos[0].append(media_diferencias) # Clculo de las desviaciones tpicas # desviacin tpica de los espesores dt_esp = calc_dt(espesores, media_espesores) # desviacin tpica de los errores dt_err = calc_dt(errores, media_errores) # desviacin tpica de las diferencias axiales dt_dif = calc_dt(diferencias, media_diferencias) estadisticos[1].append(dt_esp) estadisticos[1].append(dt_err) estadisticos[1].append(dt_dif) return estadisticos def escribir_resultados(res): s = open('medidas.dat', 'a') linea1 = '='*60 linea2 = '-'*60 s.write(linea1+'\n') s.write('{:<10}{:<14}{:<14}{} '.format(' '*18, 'Espesores', 'Error esp', 'Dif Axiales')) s.write(linea2+'\n') s.write('{}{:<17}{:<13.4}{:<15.4}{:<.4} '.format(' '*3, 'Medias', res[0][0], res[0][1], res[0][2])) s.write('\n\n') s.write('{}{:<17}{:<13.4}{:<15.4}{:<10.4}'.format(' '*3,'Desv Tipicas', res[1][0], res[1][1], res[1][2])) s.write(linea2) def principal(): # Leer el archivo web para obtener los datos url = 'http://robjhyndman.com/tsdldata/roberts/edgeman.dat' entrada = urllib.urlopen(url) datos = obtener_datos(entrada) # Escribir los datos escribir_datos_archivo(datos)
# Procesar los datos y obtener los resultados resultados = procesar_datos(datos) # Agregar los resultados al archivo de salida escribir_resultados(resultados) if __name__ == "__main__": principal()
Corramos el programa. Como resultado de la escritura en el archivo de los datos originales y de los resultados de los clculos mediante la funcin escribir_resultados, al abrir el archivo mediciones.dat con un editor de textos como gedit se tiene el siguiente contenido del archivo:
Espesor Error esp Dif axial Obs 0.3978 0.0022 0.0009 0.4019 0.0019 0.0007 0.4031 0.0031 0.0012 0.4044 0.0044 0.0011 0.3984 0.0016 0.0015 0.3972 0.0028 0.0018 0.3981 0.0019 0.0017 0.3947 0.0053 0.0022 0.4012 0.0012 0.0014 0.4043 0.0043 0.0018 0.4051 0.0051 0.001 0.4016 0.0016 0.0006 0.3994 0.0006 0.0007 0.3999 0.0001 0.0011 0.4062 0.0062 0.0014 0.4048 0.0048 0.0012 0.4071 0.0071 0.0011 0.4015 0.0015 0.0015 0.3991 0.0009 0.001 0.4021 0.0021 0.0009 0.4009 0.0009 0.0013 0.3988 0.0012 0.0014 0.3994 0.0006 0.0016 0.4016 0.0016 0.0011 0.401 0.001 0.0019 =============================================================== Espesor Error espesor Dif Axial --------------------------------------------------------------Media: 0.4012 0.00256 0.001284 Desv tpica: 0.002959 0.001898 0.0003957 ===============================================================
Los ejemplos desarrollados cubren slo una pequea variedad de las situaciones que se pueden afrontar en el procesamiento de datos de archivos. Esta entrada es la penltima de la serie sobre archivos. En la prxima estar realizando un ejemplo comprensivo. Hasta entonces y que Estn Bien!. No olviden los comentarios