Este documento describe el uso de las bibliotecas NumPy y SciPy en Python para aplicaciones científicas e ingenieriles. Explica las funciones principales de NumPy como matrices multidimensionales y funciones matemáticas avanzadas. También cubre algunas funciones de SciPy como generar números aleatorios y realizar operaciones estadísticas. Finalmente, discute los desafíos y soluciones al implementar una aplicación web que integra Flask, Angular, NumPy y SciPy, como separar las dependencias para el despliegue en la nube.
1. Inteligencia Artificial
Msc. Ing. Luis Lorgio Cárdenas Miranda
NUMPY Y SCIPY:
PYTHON PARA
CIENTIFICOS E
INGENIEROS
___
Miguel Benjamin Atencio Vargas - 12001-1
Aldo Jhenry Mamani Cabana - 12031-4
Jhoan Suarez Mamani - 12008-8
2. 2
1.INTRODUCCIÓN
El presente trabajo se refiere al uso de dos herramientas muy importantes las cuales son
ampliamente utilizadas en el campo de la ciencia y de la ingeniería.
Las características principales de estas herramientas son dar mayor soporte al lenguaje de
programación: Python otorgándole un conjunto de bibliotecas y funciones matemáticas de alto
nivel.
El análisis de estas herramientas se realizo por el interés de conocer su uso y funcionalidad en
el campo de la Inteligencia Artificial ya que el buen manejo y saber su amplia utilidad hace que
el desarrollo de la misma sea más eficiente. Profundizar la indagación desde la perspectiva de la
Inteligencia Artificial, fue de interés académico. Asimismo, nos interesamos por a integrar
herramientas diferentes para mostrar la funcionalidad de NumPy y scipy.
Para desarrollar la aplicacion tuvimos que investigar qué herramientas se acomodan mejor a
nuestras necesidades lo cual nos indujo a realizar una serie de investigaciones a:
documentacion oficial, videos, articulos, blogs, etc del ámbito de Python y Angular.
Para el análisis de las librerias utilizadas en el proyecto solo nos basaremos en NumPy y scipy,
los frameworks como flask y angular y otras funcionalidades no serán detallados ya que no el
objetivo de este trabajo.
2. Analisis de las librerías utilizadas
Numpy.- Es una biblioteca Open Source(licencia BSD) fundamental para la computación
científica con Python. Contiene entre otras cosas:
● Un poderoso objeto de matriz N-dimensional
● Funciones sofisticadas (difusión)
● Herramientas para la integración de código C / C ++ y Fortran.
● Álgebra lineal útil, transformada de Fourier y capacidades de números aleatorios.
Además de sus obvios usos científicos, NumPy también puede usarse como un eficiente
contenedor multidimensional de datos genéricos. Se puedendefinir tipos de datos arbitrarios.
Esto permite que NumPy se integre a la perfección con una amplia variedad de bases de datos.
La Documentación de NumPy esta muy bien realizada la cual incluye una guia de usuario,
documentación de referencia completa, una guía para desarrolladores se puede encontrar un
archivo completo de la documentación para todas las versiones de NumPy desde el 2009.
3. 3
La comunidad de usuarios y desarrolladores de NumPy es muy grande y está descentralizada y
se intenta dirigir conversaciones a ciertos canales como en StackOverFlow y en GitHub.
En el manual de referencia detalla funciones, módulos y objetos incluidos en NumPy, que
describen qué son y que hacen.
La transpuesta de una matriz:
NumPy.transpose(a, axes = None)
Parámetros: a : array_like
Matriz de entrada
axes: lista de entradas, opcional
Por defecto, invierta las dimensiones, de lo contrario permute los ejes de
acuerdo con los valores dados.
Retorna: p : ndarray
A con sus ejes permutados. Se devuelve una lista siempre que sea
posible.
La diagonal de una matriz:
NumPy.diagonal( a , offset = 0 , axis1 = 0 , axis2 = 1 )
Parámetros: a : array_like
Array del que se toman las diagonales.
offset : int, opcional
Desplazamiento de la diagonal desde la diagonal principal. Puede ser
positivo o negativo. Por defecto es la diagonal principal (0).
axis1 : int, opcional
Eje que se utilizará como primer eje de las subreglas 2D a partir de las
cuales se deben tomar las diagonales. Por defecto al primer eje (0).
axis2 : int, opcional
Eje que se utilizará como segundo eje de las subreglas 2D a partir de
las cuales se deben tomar las diagonales. Por defecto al segundo eje
(1).
4. 4
Devoluciones: array_of_diagonals : ndarray
Si a es 2-D, entonces se devuelve una matriz 1-D que contiene la
diagonal y del mismo tipo que a, a menos que a sea una matriz, en
cuyo caso se devuelve una matriz 1-D en lugar de una (2-D) matrix en
orden para mantener la compatibilidad hacia atrás.
Si , entonces se eliminan las dimensiones especificadas por axis1 y
axis2 , y se inserta un nuevo eje en el extremo correspondiente a la
diagonal.a.ndim > 2
Plantea: ValueError
Si la dimensión de a es menor que 2.
Inversa de una matriz: Calcular el inverso (multiplicativo) de una matriz.
NumPy.linalg.inv( a )
Parámetros: a : (…, M, M) array_like
Matriz para ser invertida.
Devoluciones: ainv : (…, M, M) ndarray o matriz
(Multiplicativa) inversa de la matriz a .
Plantea: LinAlgError
Si a no es cuadrado o la inversión falla.
Triángulo superior de una matriz: Devuelve una copia de una matriz con los elementos debajo
de la diagonal k -th puesta a cero
NumPy.triu( m , k = 0 )
5. 5
Triángulo inferior de una matriz: Devuelve una copia de una matriz con elementos por encima
de la k -ª diagonal puesta a cero.
NumPy.tril( m , k = 0 )
Parámetros: m : array_like, forma (M, N)
Matriz de entrada
k : int, opcional
Diagonal por encima de la cual cero elementos. k = 0 (el valor
predeterminado) es la diagonal principal, k <0 está debajo de ella y
k> 0 está arriba.
Devoluciones: tril : ndarray, forma (M, N)
Triángulo inferior de m , de la misma forma y tipo de datos que m .
Calcular el determinante de una matriz.
NumPy.linalg.det( a )
Parámetros: a : (…, M, M) array_like
Matriz de entrada para calcular los
determinantes para.
Devoluciones: det : (…) array_like
Determinante de a .
El rango de una matriz:
NumPy.linalg.matrix_rank(M, tol=None, hermitian=False)
6. 6
Parámetros: M : {(M,), (..., M, N)} array_like
vector de entrada o pila de matrices
tol : (…) array_like, float, opcional
Umbral por debajo del cual los valores SVD se consideran cero. Si tol es None,
y Es una matriz con valores singulares para M , y epses el valor épsilon para el
tipo de datos S, entonces tol se establece en .S.max() * max(M.shape) * eps
hermitian : bool, opcional
Si es Verdadero, se supone que M es hermitiano (simétrico si tiene un valor
real), lo que permite un método más eficiente para encontrar valores singulares.
Por defecto es falso.
Matriz o norma vectorial: Esta función es capaz de devolver una de ocho normas matriciales diferentes, o
una de un número infinito de normas vectoriales (descritas a continuación), dependiendo del valor del ord
parámetro.
NumPy.linalg.norm( x , ord = None , axis = None , keepdims = False )
Parámetros: x : array_like
Matriz de entrada Si el eje es Ninguno, x debe ser 1-D o 2-D.
ord : {int no-cero, inf, -inf, 'fro', 'nuc'}, opcional
Orden de la norma (ver tabla abajo Notes). inf significa el objeto inf de
NumPy .
eje : {int, 2-tuple of its, None}, opcional
Si el eje es un entero, especifica el eje de x a lo largo del cual calcular
las normas del vector. Si el eje es un 2-tuple, específica los ejes que
contienen matrices 2-D, y se calculan las normas matriciales de estas
matrices. Si el eje es Ninguno, se devuelve una norma de vector
(cuando x es 1-D) o una norma de matriz (cuando x es 2-D).
keepdims : bool, opcional
Si se establece en Verdadero, los ejes sobre los que se normaliza se
dejan en el resultado como dimensiones con el tamaño uno. Con esta
opción el resultado se transmitirá correctamente contra la x original .
Devoluciones: n : flotar o ndarray
Norma de la matriz o vector (s).
NumPy.dot(a, b, out = None)
Producto puntual de dos matrices.
7. 7
Parámetros: a : array_like
Primer argumento
b : array_like
Segundo argumento.
fuera : ndarray, opcional
Argumento de salida. Este debe tener el tipo exacto que se devolvería si no
se utilizara. En particular, debe tener el tipo correcto, debe ser contiguo a C, y
su tipo debe ser el tipo que se devolvería para el punto (a, b) . Esta es una
característica de rendimiento. Por lo tanto, si estas condiciones no se
cumplen, se genera una excepción, en lugar de intentar ser flexible.
Devoluciones: salida : ndarray
Devuelve el producto punto de a y b . Si a y b son ambos escalares o ambas
matrices 1-D, se devuelve un escalar; de lo contrario se devuelve una matriz.
Si fuera se da, entonces se devuelve.
Plantea: ValueError
Si la última dimensión de a no es del mismo tamaño que la segunda
dimensión de b
Scipy.- es un ecosistema basado en Python de software de código abierto para matemáticas,
ciencia e ingeniería. Se compone de herramientas y algoritmos matemáticos.
SciPy contiene módulos para optimización, álgebra lineal, integración, interpolación, funciones
especiales, FFT, procesamiento de señales y de imagen, resolución de EDOs y otras tareas para
la ciencia e ingeniería.
SciPy se basa en el objeto de matriz NumPy y es parte del conjunto NumPy, que incluye
herramientas como Matplotlib, pandas y SymPy, y un conjunto en expansión de bibliotecas de
computación científica.
Generar muestras aleatorias a partir de una función de densidad de probabilidad utilizando el
método de relación de uniformes.
scipy.stats.rvs_ratio_uniforms( pdf , umax , vmin , vmax , tamaño = 1 , c =
0 , random_state = None )
Parámetros
pdf function
Una función con firma pdf (x) que es la función de densidad de probabilidad de la distribución.
float umax
El límite superior del rectángulo delimitador en la dirección u.
vmin float
8. 8
El límite inferior del rectángulo delimitador en la dirección v.
vmax float
El límite superior del rectángulo delimitador en la dirección v.
tamaño int o tupla de ints, opcional
Definición del número de variables aleatorias (por defecto es 1).
flotador c , opcional.
Parámetro de cambio del método de relación de uniformes, ver Notas. El valor predeterminado es 0.
random_state int o np.random.RandomState instancia, opcional
Si ya es una instancia de RandomState, úsala. Si semilla es un int, devuelve una nueva instancia de
RandomState sembrada con semilla. Si Ninguno, use np.random.RandomState. El valor
predeterminado es Ninguno.
Devoluciones
rvs ndarray
Las variables aleatorias se distribuyen de acuerdo a la distribución de probabilidad definida por el
pdf.
Una variable aleatoria normal multivariante.
x = np.linspace(0, 5, 10, endpoint=False)
>>> y = multivariate_normal.pdf(x, mean=2.5, cov=0.5);
Una variable aleatoria continua normal.
rv = norm()
ax.plot(x, rv.pdf(x), 'k-', lw=2 )
ax.hist(r, density=True, histtype='stepfilled', alpha=0.2)
ax.legend(loc='best', frameon=False)
plt.show()
9. 9
3. Discusión de los resultados
La primera idea que se planteo fue hacer la aplicación en un entorno de escritorio pero se
presento en contraposición desarrollar una aplicacion web. Lo primero que se nos ocurrió fue
utilizar la pila de tecnologías MEAN pero el problema fue que dentro de NodeJS no se puede
instalar las herramientas que necesitábamos es decir NumPy y scipy entonces fue investigar
frameworks como Django y descubrimos Flask que es un micro framework para Python basado
en Werkzeug, Jinja 2. Al ser un entorno que ejecutaba python pudimos instalar cualquier
herramienta que necesitábamos. Una vez que tuvimos ello tuvimos que ver si realmente
podíamos integrar Flask con Angular para ello disponíamos de muchas formas de hacerlo
encontramos con muchos blogs, videos al final la mejor opción fue crear un entorno virtual en
este caso utilizamos Virtualenv (Es una herramienta para crear entornos aislados de Python) así
tuvimos la facilidad de hacer el deployment en la Nube para ello utilizamos Heroku para el
back-end y GitHub Pages para el front-end. Posteriormente se logro hacer la conexión y enviar
los request y responses necesarios a partir de ahi se empezo a trabajar con ambas peticiones al
mismo tiempo se empezaron a presentar complicaciones como los tipos de datos que envía
angular al backend para ello tuvimos que implementar otra funcionalidad de flask que es el
CORS para habilitar esas peticiones. Posteriormente el backend nos presenta otra complicación
indicandonos que el tipo de dato Array de NumPy no es un dato serializable para convertirlo al
formato JSON para solucionar ello utilizamos la librería de pandas:
res = pd.DataFrame(res).to_json( orient='split')
con eso implementado proseguimos con la creación de los demas metodos para ejemplificar las
funcionalidades de NumPy y scipy. Al terminar la version 0.0.1 nos decidimos por hacer el
deploy para ver que lo que estábamos haciendo iba a funcionar realmente en la Nube para así
10. 10
asegurarnos de proseguir. Ahí fue cuando nos encontramos con el siguiente problema ya que al
hacer la creacion del entorno virtual no hicimos la separación de del proyecto y las
dependencias eso nos provocó tener que subir a Heroku todas las librerías de python incluido
python subiendo alrededor de 200MB a la nube y al hacer el build nos presentó varios errores,
entonces tuvimos que rehacerparte del servicio lo solucionamos separando ambas cosas. Una
vez dejamos el servicio funcionando pasamos al desplegar el frontend en github pages donde se
nos volvió a presentar un error que ensu momento no entendimos la fuente (el problema era
que no ejecutaban las órdenes en la consola con permisos de administrador) entonces
decidimos hacer el despliegue utilizando heroku pero al encontrar el modo de hacer correr la
app teníamos que crear un servidor con express cosa que de implementarse creaba una capa
mas en la conexión del backend y el frontend entonces volvimos a la opción de GitHub Pages
que haciendo un poco de depuración de los distintos errores que fueron presentándose
logramos servir la app, de los errores el primero que nos impedía hacer el despliegue fue
otorgarle permisos de administrador, luego no apuntar bien el commit al repositorio y luego no
saber que github cuando intentas desplegar GitHub Pages crea otra rama en el repositorio
entonces teníamos que ir al repositorio y cambiar la rama de despliegue. una vez todo
desplegado procedimos a trabajar localmente.
4. Conclusiones
1. En general trabajar con python nos pareció muy comodo ya que es un lenguaje bastante
flexible pero esa flexibilidad también es la que trae de vez en cuando algunos problemas al no
poder depurar bien los errores caso que se puede depurar implementando un try catch aunque
hace que el código sea poco escalable. Fuera de ello lo fácil que es implementar un servidor nos
sorprendió.
2. En cuanto a las funcionalidades que ofrece NumPy y SciPy son altamente potentes y rápidas
en cuanto al procesamiento de grandes cantidades de datos funcionalidades que en un entorno
de utilidad y sabiendo que es lo que se necesita llegan a ser útiles, eneste trabajo solo
manejamos estas con el fin de ejemplificar su uso.
3. El trabajar con ambas herramientas (Flask y Angular ) dificulto al principio la integración
pero una vez resueltas ambas puede convivir muy bien.
5. Bibliografía
Consulta de recursos de NumPy y scipy:
https://es.wikipedia.org/wiki/NumPy
https://www.NumPy.org/
https://docs.scipy.org/doc/NumPy/reference/generated/NumPy.diagonal.html
https://es.wikipedia.org/wiki/SciPy
https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.norm.html
12. 12
m[i][j]=aux[c]
if c+1 < len(aux):c+=1
return m
def suma(a,b):
try:
global message
message = 'La suma se realizo correctamente:'
return a+b
except:
message = '(Error) Las dimensiones de sus matrices no son iguales
shape(a)={} shape(b)={} '.format(shape(a), shape(b))
def resta(a,b):
try:
global message
message = 'La resta se realizo correctamente:'
return a-b
except:
message = '(Error) Las dimensiones de sus matrices no son iguales
shape(a)={} shape(b)={} '.format(shape(a), shape(b))
def multiplicacion(a,b):
try:
global message
message = 'Se realizo el producto vectorial correctamente'
return a*b
except:
message = '(Error) No se cumple la condicion de que las columnas de
la matriz A sea igual a las filas de la matriz B. shape(a)={}
shape(b)={}'.format(shape(a), shape(b))
def producto_escalar(a,b):
try:
global message
message = 'Se realizo el producto escalar correctamente'
res=0;a=array(a);b=array(b)
for i in range(a.shape[0]):
for j in range(a.shape[1]):
res+= a[i][j]*b[i][j]
return [res]
except:
message = '(Error) Las dimensiones de sus matrices no son iguales
shape(a)={} shape(b)={} '.format(shape(a), shape(b))
13. 13
def transpuesta(a):
try:
global message
message = 'La transpuesta se resolvio correctamente'
return a.transpose()
except:
message = '(Error) Ocurrio un error al tratar de encontrar la
transpuesta de su matriz'
def diagonall(a):
try:
global message
message = 'La diagonal se resolvio correctamente'
return diagonal(a)
except:
message = '(Error) Ocurrio un error al tratar de encontrar la
diagonal de su matriz'
def inversa(a):
try:
global message
message = 'La inversa se resolvio correctamente'
return linalg.inv(a)
except:
message = '(Error) Ocurrio un error al tratar de encontrar la
inversa de su matriz. Verifique que la matriz sea cuadrada'
def triangular(a, c):
try:
global message
message = 'La triangular se resolvio correctamente'
if c == 'i': return tril(a)
else: return triu(a)
except:
message = '(Error) Ocurrio un error al tratar de encontrar la
triangular de su matriz. Verifique que la matriz sea cuadrada'
#-------------escalar------------
def determinante(a):
try:
global message
message = 'El determinante se resolvio correctamente'
return [[linalg.det(a)]]
except:
message = '(Error) Ocurrio un error al calcular el determinante.
14. 14
Verifique su matriz enviada'
def rango(a):
try:
global message
message = 'El rango se resolvio correctamente'
return [[linalg.matrix_rank(a)]]
except:
message = '(Error) Ocurrio un error al calcular el rango. Verifique
su matriz enviada'
def norma(a):
try:
global message
message = 'La norma se resolvio correctamente'
return [[linalg.norm(a)]]
except:
message = '(Error) Ocurrio un error al calcular norma. Verifique su
matriz enviada'
#----------Sistemas Lineales-----------
def lineal(a,b):
try:
global message
message = 'El sistema de ecuaciones se resolvio correctamente'
return linalg.inv(a).dot(b)
except :
message = '(Error) Ocurrio un error al resolver el sistema.
shape(A)={} shape(B)={} '.format(shape(a), shape(b))
def arrayNormal(m,n):
global message, name
message = 'Se creo el archivo correctamente'
name = 'Distribucion Normal Scipy'
f = stats.norm.pdf
v_bound = np.sqrt(f(np.sqrt(2))) * np.sqrt(2)
umax, vmin, vmax = np.sqrt(f(0)), -v_bound, v_bound
rvs = []
for i in range(m):
print(n)
rvs.append(stats.rvs_ratio_uniforms(f, umax, vmin, vmax, size=n))
return rvs
def arrayMulti(m,n):
15. 15
global message, name
message = 'Se creo el archivo correctamente'
name = 'Multivariate_normal Scipy'
f = stats.multivariate_normal.pdf
v_bound = np.sqrt(f(np.sqrt(2))) * np.sqrt(2)
umax, vmin, vmax = np.sqrt(f(0)), -v_bound, v_bound
rvs = []
for i in range(m):
print(n)
rvs.append(stats.rvs_ratio_uniforms(f, umax, vmin, vmax, size=n))
return rvs
class Employees(Resource):
def get(self):
return 'Servicio funcionando :D'
class Operacion(Resource):
def post(self):
global message
if request.method == "POST":
resp = request.get_json()
a = matrix(clean(resp['matrix'][0]))
b = matrix(clean(resp['matrix'][1]))
op = resp['operacion']
if op == '+': res = suma(a,b)
if op == '-': res = resta(a,b)
if op == '*': res = multiplicacion(a,b)
if op =='lineal': res = lineal(a,b)
if op == 'escalar': res = producto_escalar(a,b)
res = pd.DataFrame(res).to_json( orient='split')
return {'resultado': res, 'mensaje': message}
class Unimatriz(Resource):
def post(self):
global message
if request.method == "POST":
resp = request.get_json()
a = matrix(clean(resp['matrix'][0]))
op = resp['operacion']
if op == 'transpuesta': res = transpuesta(a)
if op == 'diagonal': res = diagonall(a)
if op == 'inversa': res = inversa(a)
16. 16
if op == 'triangularI': res = triangular(a, 'i')
if op == 'triangularS': res = triangular(a, 's')
if op =='determinante': res = determinante(a)
if op =='rango': res = rango(a)
if op =='norma': res = norma(a)
res = pd.DataFrame(res).to_json( orient='split')
return {'resultado': res, 'mensaje': message}
class Random(Resource):
def post(self):
global message, name
if request.method == 'POST':
resp = request.get_json()
m = int(resp['m']); n = int(resp['n'])
a = int(resp['a']); b = int(resp['b']); tipo = resp['type'];
if tipo == 'float':
message='Se creo la tabla de excel de valores de tipo float
con los siguientes valores: filas:{}, columnas:{}, rango: ({}, {})
'.format(m,n,a,b)
name='Float({}, {})'.format(n,m)
res = (1+b-a) * np.random.random_sample((m, n)) + a
if tipo == 'normal':
name='Normal({}, {})'.format(n,m)
mu = resp['mu']; sigma = resp['sigma']
message='Se creo la tabla de excel de distribucion normal
con los siguientes valores: filas:{}, columnas:{}, mu:{}, sigma:{}
'.format(m,n,mu,sigma)
res=[]
for i in range(m):
res.append(np.random.normal(mu, sigma, n))
if tipo=='normal_Scipy': res = arrayNormal(m,n)
if tipo=='Multivariate_normal': res = arrayMulti(m,n)
if tipo =='Integer':
name='Integers({}, {})'.format(n,m)
res = np.random.randint(a,b+1, size=(m, n))
message='Se creo la tabla de excel de valores de tipo int
con los siguientes valores: filas:{}, columnas:{}, rango: ({}, {})
'.format(m,n,a,b)
res = pd.DataFrame(res).to_json( orient='split')
return {'data': res, 'message': message, 'name': name}
17. 17
Server
from flask import Flask
from flask_restful import Api
from flask_cors import CORS
from controllers import *
PORT = 5000
DEBUG = False
app = Flask(__name__)
CORS(app)
api = Api(app)
api.add_resource(Employees, '/')
api.add_resource(Operacion, '/operacion', methods = ["POST"])
api.add_resource(Unimatriz, '/unimatriz', methods = ["POST"])
api.add_resource(Random, '/random', methods = ["POST"])
if __name__ == '__main__':
app.run(port=PORT, debug=DEBUG)
El repositorio del backend se encuentra en:
https://github.com/skyfall947/backend-numpy
El repositorio de frontend se encuentra en:
https://github.com/skyfall947/numpy