jueves, 25 de febrero de 2021

Función Print(), caracteristicas y formateo de una cadena de texto. Métodos de manipulación de cadenas.

 

 

PRINT()

 

En los próximos post voy a transcribir los apuntes que me he ido haciendo a medida que estaba aprendiendo python. Espero que puedan seros de utilidad. Son muy sencillos y están hechos a modo de chuleta para poder consultar cuando se necesite.

Puedes consultar la documentación oficial para conocer todos los métodos String disponibles.

Empezamos.

 

La función Print de python y formatos de salida de cadenas de texto o string.

 

La función print() nos sirve generalmente para mostrar un mensaje generalmente en pantalla, aunque también se podría hacer en cualquier otro dispositivo de salida estándar.  El mensaje a mostrar puede ser un string (texto) o cualquier otro objeto que previamente debe ser convertido a string antes de ser visualizado en la pantalla.

Podemos pasar los valores asignados a una variable como parámetros de la función print(). Podemos poner tantos parámetros como queramos separados por comas. Si lo hacemos de esta forma, automáticamente python nos pone un espacio en blanco después de cada coma.

 

>>> nombre = "Andrés"

>>> edad = 40

>>> print("Mi nombre es", nombre, "y tengo" , edad, "años")

Mi nombre es Andrés y tengo 40 años

 

Si tenemos solamente cadenas de texto podemos concatenarlas o unirlas usando el signo "+" pero ten en cuenta que se unirán tal cual estén sin que automáticamente se pongan espacios en blanco. Por ejemplo:

 

>>> nombre = "Andrés"

>>> edad = "40"

>>> print("Mi nombre es"+ nombre +"y tengo"+ edad + "años")

Mi nombre esAndrésy tengo40años 

 

Podemos justificar el texto de cualquier string usando los siguientes métodos aplicados al string en cuestión: 

 

-         .rjust(n) à justificar el texto a la derecha

-         .center(n) à centrar el texto

-         .ljust(n) à justificar el texto a la izquierda.-          

 

En donde n es el tamaño de la cadena a mostrar que tendrá que ser mayor que el texto. Esto se suele utilizar para mejorar la presentación de los datos mostrados por pantalla.

 

Ejemplo de .rjust(n) 

>>> cadena = "El perro de San Roque"

>>> cadena.rjust(50)

                             El perro de San Roque

 

Ejemplo de .center(n) 

>>> cadena = "El perro de San Roque"

>>> cadena.center(50)

              El perro de San Roque  

 

Ejemplo de .ljust(n) 

>>> cadena = "El perro de San Roque"

>>> cadena.ljust(50)

El perro de San Roque 

 

 

Insertar el valor de una variable dentro de una cadena de texto.

 

 

Puedes encontrar toda esta información ampliada en el documento de python PEP 3101.

 

 

En muchas ocasiones necesitaremos imprimir por pantalla un texto pero dentro del mismo querremos introducir el valor de variables.

 

Esto, podemos hacerlo de 3 formas:

 

1)    Utilizando el operador “%”. Pasamos los valores a imprimir indicando de que tipo es la variable a mostrar y pasando los datos a imprimir como una tupla ( ) o un diccionario { }

 

Carácter

Tipo de significado

s

Cadena de Texto

d

Número Entero

f

Número Flotante

e

Número Real en formato exponencial

o

Número Octal

x

Número Hexadecimal

 

 

Pasando los datos como una tupla () o un diccionario {}

 

>>> nombre = "Andrés"

>>> edad = 45

>>> print('Su nombre es %s y tiene %d años' %(nombre, edad))

Su nombre es Andrés y tiene 45 años 

>>> nombre = "Andrés"

>>> edad = 45

>>> print('Su nombre es %(name)s y tiene %(age)d años' %{'name':nombre, 'age':edad})

Su nombre es Andrés y tiene 45 años             

     

2)    Utilizando el método str.format().

Se utiliza de una forma muy parecida al anterior. 

 

>>> nombre = "Andrés"

>>> edad = 45

>>> print('Su nombre es {0} y tiene {1} años'.format(nombre,edad))

Su nombre es Andrés y tiene 45 años

   En donde {0} hace referencia al primer argumento de la función format, en nuestro caso la variable nombre y {1} hace referencia al segundo argumento, edad. Si no los especificas, es decir ponemos solo los {} sin número, los va cogiendo por orden, en nuestro caso en el primero iría el nombre y en el segundo la edad. No obstante con el número dentro del {} haces referencia a que variable se insertará, lo puede especificar tu.

Otra forma de hacerlo sería la siguiente:

 

>>> nombre = "Andrés"

>>> edad = 45

>>> print('Su nombre es {name} y tiene {age} años'.format(name=nombre,age=edad))

Su nombre es Andrés y tiene 45 años

    

 

     3) F-strings. Formatear el string. Para mí es la forma más sencilla, solo tenemos que poner una f delante del texto, que en python siempre va entre " " e insertar dentro de {} las variables a mostrar.

 

>>> nombre = "Andrés"

>>> edad = 45

>>> print(f'Su nombre es {nombre} y tiene {edad} años')

Su nombre es Andrés y tiene 45 años

 

 

Dar formato a una cadena de texto.

 

Hay ocasiones en que es útil el dar un determinado formato a las cadenas de caracteres a la hora de imprimirlas por pantalla para hacerlo más legible. Esto se puede conseguir con cualquiera de los 3 métodos anteriores, aunque por modernidad y rapidez yo voy a usar el tercero. 

Para dar formato a un string simplemente ponemos ":" después de la variable y dentro de los {} y añadimos los parámetros que nos interesen para darle formato. Para que quede clara la idea vamos a ver un ejemplo sin formatear y otro aplicando un formato.

Creamos una lista con 5 números enteros y queremos mostrarlos por pantalla junto a su suma, como si los estuviéramos sumando en papel. 

Sin formato:   

  >>> lista = [300040017_000257]  

      >>> for i in lista:   

      >>>     print(i)  

      >>> print("__________")   

      >>> print(sum(lista))

   

        3000

        400

        17000

        25

        7

     __________

        20432      

 

Si queremos que quede como cuando realizamos una suma en papel tenemos que mostrar los números alineados a la derecha. Esto lo conseguimos formateando la salida de esta forma: 

       

   >>> lista = [300040017_000257]

       >>> for i in lista:

       >>>   print(f"{i:10}")

       >>> print("__________")

       >>> print(f"{sum(lista):10}")     

 

                      3000

                        400

                    17000

                          25

                            7

                __________

                    20432

     

 

Cuando ponemos entre llaves la variable a sustituir podemos agregar luego los dos puntos y la cantidad de espacios que ocupará la variable. Por defecto dentro de ese espacio definido los caracteres se alinean a la derecha ocupando los espacios. También podemos colocar entre {} la llamada a una función (la función sum retorna la suma de todos los valores de la lista.

Detrás de los ':' podemos definir todos estos argumentos. Todos ellos son opcionales y su orden de aparición sería el siguiente:

 

[[relleno]alineación][signo][#][0][anchura_mínima][.precisión][tipo]

 

[[relleno]alineación]    

Los argumentos del parámetro alineación pueden ser los siguientes:

'<' - Obliga a los caracteres del campo a alinearse a la izquierda dentro de los espacios. (Por defecto)

'>' - Obliga a los caracteres del campo a alinearse a la derecha dentro del espacio disponible.

'=' - Obliga a que los caracteres de relleno ser coloquen después del signo (si lo hay) pero antes de los dígitos. Esta opción se usa para imprimir campos con el formato +000000120. Esta opción de alineación solo es válida para tipos numéricos.

'^' - Obliga a los caracteres del campo a centrarse dentro de los espacios. 

 

Solamente si usamos el parámetro alineación podemos usar junto con él, el parámetro relleno.  El carácter opcional de relleno índica el carácter que queremos que se use para rellenar el campo al ancho mínimo que hayamos definido. Como ya dijimos antes, si utilizamos este carácter de relleno, debe ir seguido de una bandera de alineación.

 

[signo]

Solo es válido para valores numéricos y puede ser uno de los siguientes:

'+'  - Indica que se debe usar el signo tanto para números positivos como negativos.

'-'  - Indica que se debe usar el signo solamente para los números negativos (por defecto).

' '  - (un espacio en blanco) Indica que se debe usar un espacio inicial para los números positivos.

 

[#] 

Si se utiliza este parámetro significa que los números enteros se formatearán de una "forma alternativa". Esto quiere decir que la salida para los números binarios, octales y hexadecimales tendrá el prefijo "Ob", "Oo" y "Ox", respectivamente.

 

[anchura_mínima]

Es un número entero que va a definir la anchura mínima del campo. Si no se especifica, el ancho del campo estará determinado por el contenido. Si el campo anchura_mínima está precedido por un carácter cero ("ose el número 0") se habilita el relleno con ceros. Esto es lo mismo que un tipo de alineación con el argumento "=" y un carácter de relleno de 0 (ceros).

 

    lista = [300040017_000-257]

    for i in lista:

      print(f"{i:010}")

    print("__________")

    print(f"{sum(lista):10}"

 

    Salida:

 

0000003000

0000000400

0000017000

-000000025

0000000007

__________

     20382

  

 

[.precisión] 

 

    Es un número decimal que indica cuantos dígitos deben mostrarse después del punto decimal.

 

Para los tipos no numéricos, el campo indica el tamaño máximo del campo; en otras palabras, cuantos caracteres se usarán del contenido del campo.

 

    La .precisión se ignora cuando se trata de números enteros.

 

 

[tipo] 

 

    Define como deben presentarse los datos.

 

    Los tipos de presentación para números enteros son: 

 

   

'b' - Binario. El número se muestra en base 2.

'c' - Carácter. Convierte el entero al correspondiente carácter Unicode antes de imprimirlo.

'd' - Entero Decimal. El entero se muestra en base 10.

'o' - Formato Octal. El número se muestra en base 8.

'x' - Formato Hexadecimal. El número se muestra en base 16, usando letras minúsculas para los dígitos superiores a 9.

'X' - Formato Hexadecimal. El número se muestra en base 16, usando letras mayúsculas paras los dígitos superiores a 9.

'n' - Número. Es lo mismo que 'd', excepto que se usa la configuración local actual para insertar el carácter se separación en los números.

'' (Ninguno espacio en blanco) - lo mismo que 'd'

 

 

Los tipos de presentación de punto flotante disponible son:

 

   

'e' - Notación Exponencial. Imprime el número en notación científica usando la letra "e" para indicar el exponente.

'E' - Notación Exponencial. Igual que el caso anterior solamente que el número se convierte en Mayúsculas.

'f' - Punto Fijo. Muestre el número como un numero de punto fijo.

'F' - Punto Fijo. Igual que el caso anterior pero excepto que el número se muestra en mayúsculas.

'g' - Formato General. Imprime el número como un número de punto fijo a nos ser que sea demasiado grande en cuyo caso se muestra en notación exponencial "e".

'G' - Formato General. El mismo caso que en el punto anterior excepto que se utiliza la presentación "E".

'n' - Número. Es lo mismo que 'g', excepto que usa la configuración regional actual para insertar el carácter de separación de los números.

'%' - Porcentaje. Multiplica el número por 100 y muestra el número en formato fijo "f", seguido del signo de porcentaje (%).

'' (Ninguno) - similar a 'g', excepto que imprime al menos un dígito después del punto decimal.

 

 

Aunque parece complicado en realidad es bastante sencillo, solo debes tener claro cómo quieres formatear la salida. Pondré un ejemplo donde aparecen gran parte de los argumentos:

 

     

        lista = [3000400.13217_000-257]

        for i in lista:

          print(f"{i:@>+10.2f}")

        print("__________")

        print(f"{sum(lista):10}")

     

Salida:

           @@+3000.00

@@@+400.13

@+17000.00

@@@@-25.00

@@@@@+7.00

__________

          20382.132

     

  Otras cosas que se pueden aplicar a la función Print().


  Cuando utilizamos esta función automáticamente imprime al finalizar de mostrar los datos por pantalla, un salto de línea. Sin embargo, puede que hay ocasiones en que nos interese que esto no ocurra y que el siguiente print() vaya a continuación del anterior. 

 

Esto se puede conseguir utilizando el parámetro end="". Dentro de las comillas podemos poner en lo que nos interese que termine la impresión de print; que termine sin más end="",  que ponga un espacio con el siguiente print end=" ", que ponga un punto, end="." o lo que se te ocurra.

 

Por ejemplo, la función print() de forma estándar mostraría: 

        print("hola")

        print("¿Cómo estás?") 

        Salida:

    

   hola 

   ¿Cómo estás?

      

Sin embargo, me gustaría que no hiciese el salto de línea y que lo escribiese todo seguido dejando un espacio en blanco entre ambos print().

           

print("hola", end=" ")

print("¿Cómo estás?")

         Salida:         

hola ¿Cómo estás?

 

   

  - En otras ocasiones puede ocurrir lo contrario, que dentro de una instrucción print() quieras hacer un salto de línea. Pues esto es tan sencillo como utilizar el carácter especial \n dentro del texto en el que quieras realizarlo.

 

      print("Esta es la primera linea\ny esta la segunda") 

      Salida: 

      Esta es la primera línea 

      y esta la segunda

 

 

Después de ver esto, te habrás dado cuenta que el parámetro por defecto de print() es end="\n"

Otros caracteres especiales que se pueden utilizar en las cadenas son:

Caracteres de escape en python.

\n    => Salto de línea.
\t    => Tabulación.
\r    => Retorno.
\f    => Salto de Página.
\v    => Tabulación vertical.
\     => Por si solo ignora el carácter que le sigue. Escapa ese carácter. Por ejemplo
para poner unas comillas en un texto y que no de error una de las formas de hacerlo es:
print('\"Este texto va entre comillas\"')       

  

Otros métodos para la manipulación de cadenas. 

Las cadenas son objetos inmutables parecidos a las tuplas. Se pueden recorrer, indexar y aplicar métodos que alteren su estructura. Veamos alguno de ellos.

Método len("cadena")

Nos indica la longitud de una cadena incluyendo los espacios en blanco.


>>> print(len("Dos y Dos"))
9


Método str.count(subcadena, [start = 0, end = len(str)])


Devuelve el número de veces que se repite la subcadena dentro de la cadena principal. Podemos acotar la búsqueda indicando la posición de inicio y fin dentro de la cadena, aunque esto es opcional. Si no lo ponemos buscará en toda la cadena.

Busca todas las subacenas que hay en el texto.

>>> print("El sueño de una noche de verano".count("a"))
2
>>> print("El sueño de una noche de verano".count("eño"))
1
>>> cadena = "El sueño de una noche de verano"
>>> print(cadena.count("o",8,len(cadena)))
2


Método str.find(valor, start = 0, end = len(str))

Devuelve la posición de la primera ocurrencia de la subcadena. Si no existe la subcadena devuelve -1.


>>> cadena = "Pienso luego existo"
>>> print(cadena.find("ego"))
9
>>> print(cadena.find("pe"))
-1

Una variante es str.rfind(valor, start, end) que devuelve el índice de la última ocurrencia de la cadena. Veamos un ejemplo de la diferencia de ambos métodos:

>>> cadena = "ab c ab"
>>> print(cadena.find("ab"))
0
>>> print(cadena.rfind("ab"))
5


Método str.index(valor, inicio, fin) y str.rindex(valor, inicio, fin). Es igual que el método anterior, solo se diferencia en que en caso de no encontrarse el valor a buscar da un error.


>>> cadena = "ab c ab"
>>> print(cadena.index("f"))
Traceback (most recent call last):
  File "main.py", line 2, in <module>
    print(cadena.index("f"))
ValueError: substring not found


Método str.zfill(n).

Se utiliza para rellenar con ceros a la izquierda una cadena de caracteres que generalmente suele ser un número. (n) es el número del tamaño del campo. El espacio sobrante entre el tamaño del campo y el tamaño de los caracteres se rellenarán con ceros. 

      >>> print("4,56".zfill(6)) 

      004,56  


Método str.capitalize().

Se utiliza para que el primer carácter de la cadena aparezca en mayúsculas y el resto en minúsculas.

>>> print("vino de AlgeCiras".capitalize())
Vino de algeciras

 

Método str.upper() o str.lower(). Se utilizan para poner todos los caracteres que conforman una cadena de texto en Mayúsculas o Minúsculas.  

        >>> print("todo en mayusculas".upper())    

         TODO EN MAYUSCULAS

        >>> print("TODO el texto en MINúSCULAS".lower())

         todo el texto en minúsculas



 Método str.swapcase().

Intercambia mayúsculas por minúsculas y viceversa.

>>> print("vino de AlgeCiras".swapcase())
VINO DE aLGEcIRAS


Método str.strip("<caracteres>").

Devuelve una copia de la cadena tras eliminar los caracteres al principio y al final de la misma especificados en <caracteres>. Si no se pone argumento elimina los espacios en blanco (\), los saltos de línea (\n) y las tabulaciones (\t) del principio y del final.

>>> print("   vino de AlgeCiras    ".strip())
vino de AlgeCiras
# Sin los espacios en blanco al principio y al final.
>>> print("ab c ab".strip("a"))
b c ab

.lstrip() y rstrip() realizan la misma función solo que el primero lo hace solo en el principio de la cadena y el segundo solo al final de la misma.


Método str.replace(x, y, [nº veces])

En una cadena remplaza el carácter x por el y el número de veces indicado. Si no se especifica el número de veces se reemplaza el caracter x por el y en TODA la cadena.


>>> print("murcielago".replace("o","a"))
murcielaga


Método str.split([separador], [max nº de veces])

Separa los elementos de una cadena en una lista. Si no especificamos un separador se sobreentiende que se utiliza un espacio en blanco. Por ejemplo para dividir una frase en las palabras que la componen podemos utilizar:

>>> print("Eloisa está debajo de un almendro".split())
['Eloisa', 'está', 'debajo', 'de', 'un', 'almendro']

print("Eloisa está debajo de un almendro".split(" ",2))
['Eloisa', 'está', 'debajo de un almendro']

Un método similar es str.splitlines() que devuelve en una lista todas las líneas de una cadena o párrafo, para entendernos. En este caso el separador se basa en los saltos de línea encontrados (\n).

Lo contrario a estos métodos seria "separador".join(iterable) que une una lista de cadenas usando un separador.

>>> lista = "Eloisa está debajo de un almendro".split()
>>> print(lista)
['Eloisa', 'está', 'debajo', 'de', 'un', 'almendro']
>>> print(" ".join(lista))
Eloisa está debajo de un almendro


Métodos str.startwith(valor) y str.endwith(valor)

Como su nombre indica devuelve True si la cadena empieza o termina con el valor introducido. False en caso contrario.


martes, 9 de febrero de 2021

10) Acelerando expresiones complejas con Numexpr





 Acelerando expresiones complejas con Numexpr


Como vimos en el post anterior, el evaluar expresiones complejas con matrices muy grandes en Numpy hace que el rendimiento del programa decaiga considerablemente.

Por un lado, si usamos una sola línea para evaluar la expresión se producirá un uso alto e ineficiente de la memoria del equipo ya que se crearán unas cuantas matrices temporales que Numpy necesita para evaluar la expresión.

Por otra parte si evaluamos la expresión realizando una operación cada vez, aunque mejor opción que la anterior, nos puede conducir a un rendimiento poco óptimo ya que se utilizarán numerosos bucles "for" del código C de Numpy. 

Para evaluar expresiones que utilicen matrices de una mejor forma podemos usar el paquete numexpr, el cual nos proporciona herramientas para evaluar de una forma rápida expresiones con matrices.

Lo primero de todo es instalar el paquete, ya que no viene por defecto en la instalación de Numpy:

>>> pip3 install numexpr


y luego podemos ver como funciona con el siguiente ejemplo.


>>> import numpy as np

# Creamos 2 matrices x e y con un millón de elementos
>>> x = np.random.random((1_000_000, 1))
>>> y = np.random.random((1_000_000, 1))

>>> import numexpr

# Calculamos la expresión ((.35*x + .85)*x - 1.6)*y - 3
>>> expresion_evaluar = numexpr.evaluate("((.35*x + .85)*x - 1.6)*y - 3") >>> print(expresion_evaluar) [[-4.15069108] [-3.09721302] [-3.14479008] ... [-3.22412767] [-4.28999688] [-3.76542973]]


La expresión se pone entre comillas y se evaluará mediante un solo bucle C. El incremento de velocidad, comparándolo con evaluar la misma expresión solo con Numpy, suele estar entre 0,95 y 4 veces más rápido. El rendimiento suele ser mayor con matrices que no caben en la memoria cache de la CPU.


Los operadores y funciones compatibles que puedes utilizar son:

+, -, *, /, **

sin, cos, tan

exp, log, sqrt









9) Arrays temporales.










 Arrays Temporales.



Cuando Numpy tiene que realizar cálculos complejos es posible que utilice arrrays temporales. Esto puede suponer un alto consumo de recursos y memoria.

Analicemos el siguiente ejemplo. 

Creamos dos arrays. Una matriz llamda a y otra llamada b, con una forma de  (1024,1024,50) que contiene 52_428_800 elementos aleatorios.

>>> import numpy as np
>>> a = np.random.random((1024, 1024, 50))
>>> b = np.random.random((1024, 1024, 50))

>>> print(a.size(a))
52428800
Ahora creamos otra matriz llamada c con esta fórmula. c = 2.0 * a - 4.5 * b y lo ejecutamos.

>>> c = 2.0 * a - 4.5 * b
Killed

Para calcular esta última línea, se crearán dos matrices temporales para ir almacenando los resultados intermedios. Primero se almacenará 2.0 * a y luego 4.5 * b. Luego intentará realizar la operación pero,
como ves, para mi ordenador esto supone un desbordamiento de memoria ya que las matrices que se generan son muy grandes. Por lo tanto vamos a replantear el ejemplo, haciendo los arrays más pequeños para poder ver el concepto que nos interesa.

>>> import numpy as np
>>> a = np.random.random((1000, 1000, 40))
>>> b = np.random.random((1000, 1000, 40))

>>> print(a.size(a))
40_000_000

Numpy reutiliza las matrices temporales que va creando para no tener que volver a usar unas nuevas. Así, si añadimos nuevas operaciones al ejemplo previo:

 c = 2.0 * a - 4.5 * b + np.sin(a)+ np.cos(b) 

Solo se necesitan dos matrices temporales para realizar el cálculo, porque previamente ya se han creado en 2.0 * a - 4.5 * b . Ahora bien, si añadimos algunos paréntesis (que no son necesarios) a la fórmula, la situación cambia y entonces se crean tres arrays temporales.

 c =  2.0 * a - 4.5 * b +(numpy.sin(a) + numpy.cos(b))

Esto no nos interesa porque es desperdiciar recursos de memoria, con lo que o bien eliminamos los paréntesis innecesarios o podríamos mover el paréntesis y colocarlo como primer elemento de la suma, lo que nos permitiría reutilizar mejor las matrices temporales que se crean.


 c = (numpy.sin(a) + numpy.cos(b)) + 2.0 * a - 4.5 * b


La moraleja de todo esto es que si quieres ahorrar memoria (sobre todo cuando la matrices son pero que muy, muy, muy grandes) y que los cálculos se realicen más rápidamente suele ser buena idea aplicar las operaciones sobre una matriz existente uno por uno. En nuestro ejemplo anterior

 c = 2.0 * a - 4.5 * b + np.sin(a)+np.cos(b)


Es mucho más eficiente hacerlo por partes en vez de todo de una vez:

c = 2.0 * a
c -= 4.5 * b
c += np.sin(a)
c += np.cos(b)



sábado, 6 de febrero de 2021

8) Nociones de algebra lineal y polinomial con Numpy.












Nociones de algebra lineal y polinomial con Numpy.


Numpy incluye muchas funciones de álgebra lineal que te pueden ser bastante útiles si te dedicas a estos menesteres.

Por ejemplo, Numpy puede calcular productos de matrices y vectores de manera eficiente (dot, vdot), calcular los valores y vectores propios de una matriz (linalg.eig, linalg.eigvals), resolver sistemas lineales (linalg.solve) o realizar la inversión de una matriz (linalg.inv) entre otras cosas.

Para ver un ejemplo vamos a resolver un sistema de ecuaciones lineales.


Tenemos tres ecuaciones:


3X + 2Y + Z  = 1
5x + 3Y + 4Z = 2
 X +  Y -  Z = 1


Podemos expresarlas como una expresión matricial del siguiente tipo A * x = b, en donde A sería la matriz de los coeficientes de las incógnitas, "x" serían las incógnitas y "b" la matriz de los términos independientes. 


Podemos expresar este sistema como arrays de Numpy de las siguiente forma:


A = np.array([[3, 2, 1],
              [5, 3, 4],
              [1, 1,-1]])
b = np.array([1, 2, 1])


Entonces, si usamos la función np.linalg.solve podemos encontrar los valores de las incógnitas.

x = np.linalg.solve(A, b)
print(x)
[-4.  6.  1.]


También se puede resolver un problema que tenga más ecuaciones que incógnitas con la función np.linalg.lstsq(A,b) la cual nos dirá si el sistema es compatible determinado o indeterminado.

Si te interesa, puedes encontrar el más información en el siguiente enlace.


Próximo Post.  Arrays Temporales.


viernes, 29 de enero de 2021

7) Números aleatorios con Numpy














Números Aleatorios con Numpy.


Numpy nos proporciona una amplia gama de funciones para generar números aleatorios en matrices. Estas funciones están disponibles en el módulo numpy.random. Este módulo nos proporciona varias funciones para construir matrices aleatorias, entre las que se incluyen:

- random = Números aleatorios uniformes.

- normal = Los números aleatorios se agrupan formando una distribución normal.

- choice = Muestra aleatoria de una matriz dada.

Veamos algunos ejemplos.

>>> import numpy as np
>>> a = np.random.random((2,2))
>>> print(a)
[[0.01642991 0.53496398]
 [0.43409542 0.88082714]]

# Si queremos elegir 100 números entre el 0 y el 9
>>> b = np.random.choice(np.arange(10), 100)
>>> print(b)
[2 8 3 7 3 5 5 3 2 7 6 1 2 4 1 4 1 5 0 0 5 6 1 1 5 3 0 2 4 5 7 7 4 9 7 5 3
 7 3 9 1 9 4 4 6 6 9 5 6 2 1 2 6 9 6 0 7 5 0 3 1 0 1 1 6 5 9 9 2 3 1 4 1 1
 0 8 0 7 8 8 3 2 0 4 2 9 4 5 5 1 0 9 0 6 4 5 9 5 2 0]


Esto sería lo básico para construir arrrays con números aleatorios, si necesitas más información la puedes encontrar en el manual de numpy aquí.


PROBLEMA

Generaremos una matriz unidimensional de 1_000 números aleatorios distribuidos uniformemente para lo que utilizaremos el módulo numpy.random

Luego usaremos numpy.mean() y numpy.std() para obtener la media y la desviación estandar de dichos números.

Calcularemos la media y la desviación estándar de los datos.

Para terminar visualizaremos los datos en forma de distribución de frecuencias usando un histograma mediante la función plt.hist()


>>> import numpy as np
>>> import matplotlib.pyplot as plt

>>> #las funciones que se suelen utilizar para generar números aleatorios son:
>>> # random rand randint
>>> # normal, randn
>>> # choice
>>> # la diferencia entre random y rand es que el primero solo le puedes decir el tamaño
>>> # y al segundo el tamaño y la forma del array
>>> matriz=np.random.rand(1000)

>>> #creamos un histograma
>>> plt.hist(matriz)
>>> print(matriz)
>>> print('media = ', np.mean(matriz)) #media
>>> print('desviación standar = ', np.std(matriz)) #desviacion standar
>>> plt.show()

[0.32448446 0.17029493 0.88975758 0.58518918 0.03291183 0.75361128
 0.52061019 0.10227153 0.04804696 0.5571141  0.95852831 0.35740444
 0.74243456 0.9005181  0.02441245 0.55563885 0.01789706 0.78057959
 0.95879197 0.58012514 0.42234119 0.45749953 0.20109267 0.83788592
 0.74747628 0.40081211 0.32264574 0.18459283 0.85711583 0.40135899
 0.5850813  0.11131699 0.41030881 0.29485806 0.35575143 0.17555042
 0.07440257 0.27566318 0.74319726 0.59116089 0.8988699  0.88648433
 0.57924085 0.71687762 0.37703163 0.00550032 0.66757755 0.36496853
 0.86996948 0.75704079 0.86827924 0.60565516 0.73171818 0.25055696
 0.26490559 0.36708881 0.27852253 0.27251585 0.73235831 0.50528246
 0.07364568 0.10426392 0.75029462 0.04938492 0.85746969 0.13634836
 0.18648673 0.02747872 0.6551644  0.88502587 0.22726501 0.74367221
 0.7782758  0.59613769 0.73653628 0.35454465 0.35902062 0.78905887
 0.78022244 0.29818898 0.44463228 0.21355751 0.07544048 0.15893986
 0.44313512 0.08344274 0.51677987 0.32827173 0.83921191 0.45044779
 0.73769016 0.35815145 0.3040427  0.00884398 0.00102806 0.20599428
 0.29262636 0.56232598 0.11748816 0.65353648]
media =  0.4549525097391814
desviación standar =  0.28265017630941747







Próximo Post. Nociones de Algebra lineal y Polinomial con Numpy.


domingo, 24 de enero de 2021

6) Lectura y escritura de datos con Numpy.





Lectura y Escritura de datos.



En este post veremos brevemente como leer datos de un archivo con numpy y también como escribirlos. Es algo que usaremos muy frecuentemente, ya sea porque hemos recogido datos de un sensor de la rapsberry pi que tenemos guardado en un fichero, porque nos lo han mandado para que los utilicemos o porque queremos separar la parte que se refiere al programa de la parte de los datos.

Numpy nos ofrece varias funciones para poder leer y escribir datos numéricos en archivos simples que tengan un diseño en formato columna. Los comentarios y los delimitadores de columna se manejan automáticamente.

Lectura.


Numpy nos ofrece varias funciones para poder cargar datos, pero la que usaremos con mayor frecuencia es la función loadtxt.

Vamos a verlo con un ejemplo. Supón que tenemos los siguientes datos en un archivo llamado datos.dat. No es más que un archivo con los siguientes datos:

# x y
-4 5
3 9
2 12
# -4 11 >> dato invalido!!
7 nan
5 21
6 20

Vamos a leer los datos en forma de array de Numpy con tan solo llamar a la función loadtxt().

>>> import numpy as np
>>> xy = np.loadtxt("datos.dat")
>>> print(xy)
[[-4.  5.]
 [ 3.  9.]
 [ 2. 12.]
 [ 7. nan]
 [ 5. 21.]
 [ 6. 20.]]


Como puedes ver es muy sencillo. Comentemos algunos aspectos de como funciona loadtxt:

- La función lee todas las líneas, saltándose las líneas que contengan comentarios es decir las que comiencen por #. Esto se podría cambiar utilizando el parámetro comments. También podrías saltar las n primeras líneas, utilizando el parámetro skiprows.

- El valor indefinido (nan) se reconoce automáticamente.

- El programa espera que los datos dentro de cada línea estén separados por espacios. Esto se podría cambiar usando el parámetro delimeter

- El tipo de datos de la matriz Numpy se elige automáticamente en función de los valores, aunque por defecto nos devolverá un array con datos de tipo float. (Si quieres cambiarlo puedes usar el argumento dtype).

Otras funciones que también sirven para leer datos son:

- La función load sirve para leer datos en el formato comprimido de Numpy, que suele tener las extensiones npy o npz.

- La función fromfile sirve para leer los datos en formato binario.

- La función genfromtxt es mucho más flexible que loadtxt y es muy útil cuando el archivo está mal formateado o faltan datos. Sin embargo en la mayor parte de los casos loadtxt es más que suficiente.


Escritura.


Si la función para leer datos es loadtxt te puedes imaginar que para escribir datos a un archivo la función será savetxt. Tiene dos argumentos obligatorios el nombre del archivo y el array a guardar.

Podemos poner un encabezado al archivo con el argumento header.

También podemos usar un delimitador distinto de los espacios, que es el establecido por defecto, usando el argumento delimiter. 

 Y tambien podemos definir el fomato de los datos que por defecto se guardan con 18 cifras decimales con el parámetro fmt.

Veámoslo todo esto en un ejemplo.

Podemos poner los argumentos de esta forma o también lo puedes poner dentro de la función como normalmente hacemos:

>>> import numpy as np

>>> argumentos = {
    'header':'ejemplo de archivo',
    'fmt':'%7.3f',
    'delimiter':','
}

Creamos el array y lo guardamos.

>>> xy = np.array([[123.3434,34.55656],[-123233.232323,223323.32],[58,4545]])
>>> np.savetxt('archivo.dat', xy, **argumentos)

Si abrimos el archivo con un editor de textos vemos que el contenido es:

# ejemplo de archivo
123.343, 34.557
-123233.232,223323.320
 58.000,4545.000

y hasta aquí este capítulo.


EJERCICIO

En este ejercicio vamos a repasar lo que hemos visto sobre como escribir y leer arrays en el disco.

Lo primero que tenemos que hacer es crear un archivo de texto que contenga los siguientes datos.

0.012 1.243

1.338 0.321

1.177 0.742

1.431 0.500

1.232 1.901

0.697 1.432

1.499 0.725

1.403 0.543

1.173 0.123

0.555 1.054

Le llamaré prueba.dat.  Como veis, este archivo, contiene pares de valores de coordenadas x e y. 

Lo primero que haremos será leer los datos del archivo en Numpy con la función numpy.loadtxt().

Lugo vamos a sustituir todos los valores y por el número 4.5 para luego volver a guardar los arrays en un nuevo archivo con la función numpy.savetxt().


SOLUCIÓN


>>> import numpy as np

>>> archivo=np.loadtxt("prueba.dat") #cargamos los datos
print(archivo)
[[0.012 1.243]
 [1.338 0.321]
 [1.177 0.742]
 [1.431 0.5  ]
 [1.232 1.901]
 [0.697 1.432]
 [1.499 0.725]
 [1.403 0.543]
 [1.173 0.123]
 [0.555 1.054]]

>>> archivo[:,1:]=4.5 #cambiamos todos los valores de y por 4.5
>>> print(archivo)
[[0.012 4.5  ]
 [1.338 4.5  ]
 [1.177 4.5  ]
 [1.431 4.5  ]
 [1.232 4.5  ]
 [0.697 4.5  ]
 [1.499 4.5  ]
 [1.403 4.5  ]
 [1.173 4.5  ]
 [0.555 4.5  ]]

# para guardar solo existen 2 argumentos obligatorios que es nombre del archivo
# y array para guardar los datos que por defecto lo hace con 18 números decimales
# pero se puede modificar con el comando fmt '%entero.decimalf' parecido al format de strings
>>> np.savetxt("ejercicio.dat",archivo, fmt='%1.3f')



Próximo Post. Números aleatorios en Numpy.