Mostrando entradas con la etiqueta Herramientas. Mostrar todas las entradas
Mostrando entradas con la etiqueta Herramientas. Mostrar todas las entradas

martes, 1 de abril de 2025

Python. Recursos.

Biblioteca openpyxl (2). Acceso a datos.

La segunda parte, y a la vez la inversa del proceso descrito en la entrada anterior, es el acceso a un libro Excel y a los datos contenidos en cualquiera de sus hojas. Existen diferentes recursos Python para hacerlo, pero por ahora nos limitaremos a la biblioteca openpyxl.



Lo primero que tenemos que hacer es acceder al módulo que nos permite cargar un libro Excel mediante la instrucción correspondiente (from openpyxl import load_workbook), que, mediante su función load_workbook() permite acceder con facilidad a cualquier archivo identificado como .xlsx. Esta función recibe un parámetro asociado al atributo filename que asocia la dirección del archivo (load_workbook(filename = 'Libro2.xlsx') -> en este caso se supone que el archivo se encuentra en el mismo directorio que el script). Para facilitar el manejo del objeto, lo referenciamos en una variable, lo que facilita el trabajo posterior (vg. libro = load_workbook(filename = 'Libro2.xlsx'))

El segundo paso consiste en acceder a la hoja en la que se encuentran nuestros datos.  Esto podemos hacerlo de dos formas diferentes:
  • Accediendo a la hoja activa (la primera) (hoja = libro.active)
  • O identificando la hoja por su nombre (Vg. hoja =libro['Hoja0'])
Aunque cualquiera de los dos procedimientos es válido, posiblemente sea el segundo el de mayor utilidad, aunque requiere saber de antemano el nombre de cada una de las hojas a las que deseamos acceder; nada que deba suponer un problema para nosotros, teniendo en cuenta el uso que vamos a hacer de script de este tipo.

El tercer paso es en realidad un conjunto de opciones (de las que vamos a ver algunas de ellas), que tienen en común, como es de esperar, el acceso a los datos contenidos en celdas.

Y la forma más simple de hacerlo es identificando directamente la celda por su posición (A1). Así, por ejemplo, para celda1 = hoja['A1'].value, la expresión print('El dato de la celda A1 es: '+ celda1) nos muestra el contenido de dicha celda. Aunque si el contenido es un número nos dará error, siendo necesario convertir la variable a string (str(celda1)). Dado que no incurrimos en error en caso de convertir un string en string, parece conveniente emplear la función de conversión por defecto cuando el objetivo sea el manejo de cadenas de texto (concatenación), como es el caso de lo que pretendemos con la función print().

Si conocemos (y así será la mayoría de la veces) la estructura de los datos a los que deseamos acceder, podemos crear un procedimiento basado en la repetición del modelo anterior, lo cual es funcional y suficiente cuando deseemos acceder a hojas de cálculo diseñadas como registro de datos, pero puede no ser lo más adecuado cuando trabajamos con hojas de cálculo que contienen tablas, que son la mayoría de las que nos podemos encontrar. En estos casos nos van a ser de utilidad conocer otras formas de acceder al contenido.

Disponemos, en primer lugar, de un procedimiento para conocer cuantas filas y columnas hay creadas en nuestra hoja, punto de partida interesante para el trabajo posterior:
  • col_n = hoja.max_column -> print('Número total de columnas: '+str(col_n))
  • fil_n = hoja.max_row -> print('Número total de filas : '+str(fil_n))
 ... nos devuelven respectivamente sendos integer con el número de columnas y de filas. Estos datos son de interés cuando la hoja está organizada de forma simple y compacta, pero puede generar confusión cuando cuenta con tablas diferentes y de escasa identidad en cuanto a contenido. De ahí también el interés de construir tablas con sentido y unidad de criterio, aunque sea a costa de aumentar el número de hojas del libro. 

Pero bueno, esta es otra cuestión. Volvamos a lo que nos interesa, que ahora es acceder al contenido de una fila de datos, para lo cual recurrimos a una estructura compleja basada en la función cell().value y una estructura for...
  • valores_fila = [hoja.cell(row=2, column=i).value for i in range(1,hoja.max_column+1)]
... que trataré de explicar a continuación:
  • los valores obtenidos mediante la función cell() toma como referencia (en esta caso) la segunda fila (row =2) de la tabla (colección de celdas) de la hoja referenciada en la variable hoja (hoja.cell().value). 
  • Sobre las columnas iterará el bucle for que desarrolla el atributo value de la estructura (.value for i...)
  • Es este ciclo es que se encarga de recoger los valores de las columnas (de la fila indicada antes), desde la primer hasta la última de las columnas (hoja.max.column+1)
El resultado que obtenemos (print(valores_fila)) es una lista con los datos de la fila en cada una de las columnas. Si variamos el parámetro row accedemos a otra fila y si modificamos el primer valor de la función range() accedemos a la columna que indiquemos y a las sucesivas hasta finalizar la colección de columnas. De este modo podremos acceder a los datos de un determinado registro (fila) de una tabla de datos, si bien se requiere conocer el contenido de dicha tabla para que resulte realmente útil. En otro caso, serán necesarios 8por ejemplo) procedimientos de búsqueda basados en condicionales.

De modo similar podemos acceder al contenido de una columna de datos mediante una estructura simular a la anterior...
  • valores_col=[hoja.cell(row=i,column=2).value for i in range(2,7)]
... en la que...
  • usamos la misma función cell().value asociada a un ciclo for (cell().value for i...) en la que ahora la itinerancia de ciclo se realiza sobre el identificador de la fila (row=i), mientras que se establece el valor fijo de la columna (column=2 -> en este caso nos interesa al segunda columna de la tabla)
  • ...lo que repercute en el contenido de range(), que tiene como primer parámetro la segunda celda (de la segunda columna) empezando desde B1 (ergo B2) y recorre hasta la posición 7
El resultado (print(valores_col)) es la colección de nombres (columna 2) de cada uno de los registros (desde José hasta Sonia)

Para finalizar esta entrada, vamos a ver cómo podemos acceder a un conjunto de registros (filas y columnas), lo que equivale decir que a una base de datos. Este procedimiento viene a ser el resumen de los dos anteriores, pero se desarrolla de forma diferentes.
  • Primero creamos un rango de celdas (las que nos interesan para nuestro análisis) mediante un procedimiento de acotado sobre la hoja (rango_celdas = hoja['A2':'C6'])
  • Y después lo recorremos mediante for indicando explícitamente cada uno de los elementos de su estructura (for cell1, cell2,cell3 in rango_celdas:)
  • ... para (en este ejemplo) mostrarlo por pantalla (print(cell1.value, cell2.value,cell3.value))
El rango de celdas establecido identifica la fila (en este caso A2, para no acceder al encabezado de la tabla) y la columna final (en este caso C6, para recoger todos los registros de datos). La enumeración de celdas en la estructura for y en el función print() es fundamental para que efectivamente podamos acceder a ellas mediante el bucle y (en este caso) mostrar su contenido (vg. cell1.value).

Documento. Accede desde este enlace al script en el que se recoge el código analizado en esta entrada. 

miércoles, 26 de marzo de 2025

Python. Recursos.

Biblioteca openpyxl (1). Registro de datos.

Aunque es una forma un tanto grandilocuente de expresarlo, lo cierto es que en esta entrada aprenderemos a utilizar openpyxl para automatizar el registro de datos en una hoja de cálculo; el resultado es, valga la redundancia y el juego de palabras, la creación de un registro (individual) de datos. Aun no llegamos a la creación de una tabla de datos (y por tanto tampoco a crear una base da datos), pero damos un paso adelante en esta dirección.


Para crear un registro de datos basado en un libro Excel mediante un script Python, primeramente, llamar al módulo openpyxl de creación de la hoja de cálculo mediante la instrucción from openpyxl import Workbook. Hecho esto ya estamos en disposición de crear nuestra primera hoja de cálculo mediante la función (sin parámetro) Workbook(), que asociaremos a una variable para facilitar el trabajo que viene a continuación (Hoja = Workbook()). De este modo ya hemos creado una documento Excell (accesible también desde Calc), el cual contiene una hoja, por lo que ya podemos trabajar con ella, pero te aconsejo que lo siguiente que hagas sea guardar tu trabajo mediante la función save() que, esta sí, tiene como parámetro la dirección donde guardar el documento y el propio identificador de éste (nombre y extensión), todo ello asociado a la variable con la que se identifica el documento (hoja.save("Hoja1.xlsx") -> En este caso se entiende que el documento se guarda en el mismo directorio del script python). 

Aunque una única hoja puede ser suficiente para la mayoría de las tareas, habrá ocasiones en las que necesitemos disponer de más de una. Para ello necesitamos  crear más hojas mediante código, lo que es posible y sencillo mediante la función create_sheet() que recibe como parámetro el nombre que la hoja mostrará en el libro (vg. create_sheet("Hoja2")), aunque ese nombre no se debe confundir con el nombre de la variable con la que se accede a la hoja desde el script (vg. y para complicar un poco las cosas, hoja1 = hc_uno.create_sheet("Hoja2")). De este modo podremos crear tantas hojas como deseemos, bien de forma sucesiva, bien posicionándola mediante un segundo parámetro tipo integer que indica la posición (vg hoja0 = hc_uno.create_sheet("Hoja0",0) asociada a la variable hoja0 crea una hoja de nombre Hoja0 en la posición 0, esto es, en primera posición).

También podremos cambiar el nombre visible de las hojas mediante el atributo title (hoja5 = hc_uno.create_sheet("Hoja5",-1) -> hoja5.title ='Hoja3') -> Asociada a la variable hoja5 creamos una hoja en penúltima posición a la que damos por nombre Hoja5, pero después modificamos ese nombre por Hoja3, aunque en el script deberemos seguir refiriéndonos a la hoja por su variable (hoja5).

Esta diferencia es fundamental para posicionar datos en las celdas de las hojas, ya que debemos utilizar los nombres de las variables con las que éstas están referenciadas (vg. hoja5['B1'] = 'Estas en la Hoja 3') -> escribe en la celda B1 de la Hoja3 el string 'Estas en la Hoja 3' aunque para ello usamos como referencia la variable hoja5.

La anterior es la forma más sencilla y directa de escribir contenido en una celda, pero también podemos usar la función cell() asociada a la variable con la que identificamos la hoja. Esta función recibe tres parámetros: la fila, la columna y el dato (o variable) a escribir (hoja5.cell(row=2, column=2, value='Segunda forma'))

Y hablando de columnas (column) y filas (row), también podemos grabar colecciones de datos en una tabla mediante un ciclo for, acercándonos así al objetivo de crear una tabla (base) de datos.

Para ello primero establecemos una hoja como hoja activa, esto es, la hoja sobre la que copiaremos los datos (sheet = hc_uno.active) y forzamos que ésta sea la nombrada como Sheet en el libro (sheet = hc_uno['Sheet'])

... después creamos nuestra colección de datos (en esta ocasión de forma directa)...

datos = [('id','Nombre', 'Edad'),
         (0,'José',35),
         (1,'Lucas',27),
         (2,'Mario',31),
         (3,'Antonio',28),
         (4,'Sonia',24)
    ]

... y después desarrollamos un ciclo for sobre el parámetro row 

for row in datos:
    sheet.append(row)

Documento. Aunque no sea gran cosa, te dejo acceso al script que contiene el código trabajado en esta entrada. Con el tiempo ampliaremos el código para darle forma y utilidad; de momento es más puro ejercicio para aprender a manejarnos con openpyxl.



sábado, 22 de marzo de 2025

Python. Recursos.

Biblioteca OpenPyXL

Esta biblioteca es a las hojas de cálculo (Excell - Calc) lo que python-docx es a los documentos de texto. Con esto queda dicho lo fundamental que vamos a tratar en esta entrada, aunque sea necesario dar algún que otro detalle más. A ello vamos.



Y eso más que vamos a decir empieza por indicar que necesitamos instalar la biblioteca desde Símbolo del sistema mediante la instrucción pip install openpyxl. Posteriormente podremos consultar su documentación en esta página., aunque también podemos basarnos en otras más sencillas para una introducción a lo fundamental de su manejo. Esta página podría servir para tal fin.

Para trabajar con los módulos que componen la biblioteca openpyxl podemos utilizar esta instrucción (import openpyxl) que importa el módulo al completo, o utilizar este otro tipo de instrucciones (from openpyxl import workbook) que nos permite trabajar con funciones y atributos específicos, como en este caso, que permite ejecutar las principales y básicas instrucciones para automatizar el trabajo con hojas de cálculo.

Al tratarse del trabajo con hojas de cálculo, debemos tener en cuenta que nos interesa tanto crear y trasladar datos a la hoja de cálculo (input) como acceder a los datos grabados en una hoja de cálculo, dado que ésta cumple funciones de base de datos. Esto, trasladado al estudio del código básico que desarrollaremos en las entradas que siguen (empezando por ésta), implica centrar la atención en los dos procedimientos básicos: crear una hoja de cálculo como fuente de datos y acceder a los datos contenidos en esa base de datos. Parece novedoso (y lo es por comparación con python-docx y python-pptx), pero en realidad no es nada que no hayamos hecho ya trabajando con OOo Basic.


sábado, 13 de julio de 2024

Herramientas. Procedimientos.

Macro para desplazarse por las hojas

Cuando el número de hojas que contiene nuestro libro Calc es elevado, desplazarse por ellas mediante el navegador inferior puede resultar poco práctico. Como alternativa podemos crear una macro que nos facilite este desplazamiento. Además así podremos ir a la hoja que deseemos desde un comando.


Una vez [creada la macro] según lo que ya sabemos toca estudiarla para sacar de ella todo el rendimiento posible.

Sub macroIrHoja2

dim document   as object
dim dispatcher as object

document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = "Nr"
args1(0).Value = 2
dispatcher.executeDispatch(document, ".uno:JumpToTable", "", 0, args1())

End Sub

Una ver eliminados los comentarios sobradamente conocidos, y en su sustitución, paso a explicar el funcionamiento de esta macro. El objetivo que se persigue con ella es desplazar a la hoja llamada "Hoja2", por lo que se supone que nos encontramos en otra hoja diferentes (1)

Apreciamos, en primer lugar, la sintaxis común a todas la macros:

  • La declaración de las variables objeto document   y dispatcher, y la asignación de esos objetos a las variables:

document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

  • Observamos que la matriz args1(0) cuenta con dos dimensiones (Name y Value) (2): el primero toma el valor "Nr" que identifica que trabajaremos con el número de la hoja y el segundo, Value (valor) al que se asigna el dato (integer) concreto (2) (3)
  • Finalmente, el ejecutor de la macro (dispatcher.executeDispatch) nos muestra la instrucción que ejecuta la orden de saltar a la hoja señalada en args1(0).Value (".uno:JumpToTable"(4)
Una vez comprendido el código de esta macro, por ejemplo, puedes añadir más hojas a tu libro y construir un navegador implementado comandos asociados a macros. Esta que te comento a continuación no es la mejor opción, pero sí muy práctica: es suficiente con que copies y pegues la macro original las veces que desees, des nombres diferentes a los script creados y cambies el parámetro de args1(0).Value = 2 , sustituyendo así el número de orden de la hoja.

Pero también puedes transformar esta macro en una subrutina y ahorrarte un montón de líneas de código; de este modo será suficiente con llamar a la subrutina script.

Observa cómo convertí la macro original en una subrutina ( MovHoja (iH As Integer)). El script (IrHoja2) es ahora mucho más sencillo y, eso sí, tienes que repetirlo (con los cambios que corresponden) las veces que sea necesario si quieres aplicarlo a más hojas:

Sub IrHoja2 'Script

MovHoja (2)

End Sub


Sub MovHoja (iH As Integer) 'Subrutina

dim document   as object
dim dispatcher as object

document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = "Nr"
args1(0).Value = iH
dispatcher.executeDispatch(document, ".uno:JumpToTable", "", 0, args1())

End sub

El funcionamiento del conjunto es así de sencillo: desde el script se llama a la subrutina, pasando como argumento el id numérico de la hoja (6). La subrutina ejecuta el procedimiento ya visto antes en la macro, sustituyendo el contenido de args1(0).Value empelado en la macro por el identificador del parámetro que se define en la declaración de la subrutina (= iH) (7)

Documento. En [este libro Calc] encontrarás el código explicado en la entrada.

NOTAS

(1) Perogrullada: de no ser así no apreciaríamos el efecto de la macro.
(2) Como puedes ver, hasta ahora nada de especial en cuanto a la sintaxis de una macro
(3) Esto ya nos da una pista de cómo podemos manipular esta macro mediante OOo Basic.
(4) En el lenguaje macro, las hojas se asimilan a tablas, de ahí la instrucción JumpToTable.
(5) Aunque no es la única opción; y menos aun si sólo vas a trabajar con una macro.
(6) Fíjate que en el caso de las macros, el índice de la hoja coincide con su orden numérico observable: la primera es 1, no 0. Esto no es así en un script o una subrutina creado directamente en OOo Basic, ya que en él la matriz de hojas del libro se inicia con el valor 0.
(7) Con lo que, como sabes, se está asignando el valor establecido en el script en la llamada a la subrutina.