<jupyter_start><jupyter_text>MAT281 - Laboratorio N°02 Objetivos de la clase* Reforzar los conceptos básicos de numpy.  Contenidos* [Problema 01](p1)* [Problema 02](p2)* [Problema 03](p3)<jupyter_code>import numpy as np
import time
import sys<jupyter_output><empty_output><jupyter_text>Problema 01Una **media móvil simple** (SMA) es el promedio de los últimos $k$ datos anteriores, es decir, sea $a_1$,$a_2$,...,$a_n$ un arreglo $n$-dimensional, entonces la SMA se define por:$$sma(k) =\dfrac{1}{k}(a_{n}+a_{n-1}+...+a_{n-(k-1)}) = \dfrac{1}{k}\sum_{i=0}^{k-1}a_{n-i}  $$ Por otro lado podemos definir el SMA con una venta móvil de $n$ si el resultado nos retorna la el promedio ponderado avanzando de la siguiente forma:* $a = [1,2,3,4,5]$, la SMA con una ventana de $n=2$ sería:    * sma(2): [mean(1,2),mean(2,3),mean(3,4)] = [1.5, 2.5, 3.5, 4.5]    * sma(3): [mean(1,2,3),mean(2,3,4),mean(3,4,5)] = [2.,3.,4.]Implemente una función llamada `sma` cuyo input sea un arreglo unidimensional $a$ y un entero $n$, y cuyo ouput retorne el valor de la media móvil simple sobre el arreglo de la siguiente forma:* **Ejemplo**: *sma([5,3,8,10,2,1,5,1,0,2], 2)* = $[4. , 5.5, 9. , 6. , 1.5, 3. , 3. , 0.5, 1. ]$En este caso, se esta calculando el SMA para un arreglo con una ventana de $n=2$.**Hint**: utilice la función `numpy.cumsum`<jupyter_code>np.cumsum([5,3,8,10,2,1,5,1,0,2],dtype=float)
def sma(a:list,n:int)->np.array:
    
    """
    sma(a,n)

    Calcula el valor de la media móvil simple sobre un arreglo a con una venta de n y los guarda en una lista

    Parameters
    ----------
    a : list
        Lista de los valores a los cuales se les calculará la sma.
    
    n : int
        Valor de la ventana de los datos

    Returns
    -------
    output : list
        Lista con la sma calculada.
        
    Examples
    --------
    >>> sma([5,3,8,10,2,1,5,1,0,2], 2)
    array([4. , 5.5, 9. , 6. , 1.5, 3. , 3. , 0.5, 1. ])
    
    """
    
    
    l = np.cumsum(a) #Lista con la suma acumulada de los valores de a
    lista = [] #Lista vacia para agregar los valores de la sma
    for i in range(n-1,len(l)): #iteramos sobre la lista que tiene los valores acumulados
        if i == n-1: #cumsum mantiene el primer valor asi que se agrega de manera directa dividiendo por la ventana n
            lista.append(l[n-1]/n) 
        else:
            lista.append((l[i]-l[i-n])/n) #Calculamos la diferencia entre el valor de la i-n ésima posicion menos el anterior
                                          #en el sentido del largo de la venta y se divide por la ventana.  
    return np.array(lista)
            
            
sma([5,3,8,10,2,1,5,1,0,2], 2)<jupyter_output><empty_output><jupyter_text>Problema 02La función **strides($a,n,p$)**, corresponde a transformar un arreglo unidimensional $a$ en una matriz de $n$ columnas, en el cual las filas se van construyendo desfasando la posición del arreglo en $p$ pasos hacia adelante.* Para el arreglo unidimensional $a$ = [ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10], la función strides($a,4,2$), corresponde a crear una matriz de $4$ columnas, cuyos desfaces hacia adelante se hacen de dos en dos. El resultado tendría que ser algo así:$$\begin{pmatrix} 1& 2 &3 &4 \\  3&  4&5&6 \\  5& 6 &7 &8 \\  7& 8 &9 &10 \\ \end{pmatrix}$$Implemente una función llamada `strides(a,4,2)` cuyo input sea un arreglo unidimensional y retorne la matriz de $4$ columnas, cuyos desfaces hacia adelante se hacen de dos en dos. * **Ejemplo**: *strides($a$,4,2)* =$\begin{pmatrix} 1& 2 &3 &4 \\  3&  4&5&6 \\  5& 6 &7 &8 \\  7& 8 &9 &10 \\ \end{pmatrix}$<jupyter_code>def strides(a:list,n:int,p:int)->np.array:
    
    """
    strides(a,n,p)

    Toma una lista a y la convierte en una matriz de n columnas con p pasos

    Parameters
    ----------
    a : list
        Lista de los valores que se convertiran a matriz.
    
    n : int
        Cantidad de columnas.
        
    p : int
        Paso en el stride

    Returns
    -------
    output : np.array
        Matriz con el stride aplicado.
        
    Examples
    --------
    >>> strides([1,2,3,4,5,6,7,8,9],3,1)
    array([[1., 2., 3.],
       [2., 3., 4.],
       [3., 4., 5.],
       [4., 5., 6.],
       [5., 6., 7.],
       [6., 7., 8.],
       [7., 8., 9.]])
    
    """
    
    filas = int((len(a)-n+p)/p) #se calcula la cantidad de filas con una formula testeada a mano con ejemplos simples
    matriz = np.zeros((filas,n)) #creacion de la matriz
    for i in range(0,filas): #iteramos sobre las filas
        matriz[i:]=a[i*p:n+i*p] #cambia la fila i-ésima y toma el trozo correspondiente a la fila de la matriz de acuerdo al stride
    return matriz
        
#ejemplo
strides([1,2,3,4,5,6,7,8,9],3,1)<jupyter_output><empty_output><jupyter_text>Problema 03Un **cuadrado mágico** es una matriz de tamaño $n \times n$ de números enteros positivos tal que la suma de los números por columnas, filas y diagonales principales sea la misma. Usualmente, los números empleados para rellenar las casillas son consecutivos, de 1 a $n^2$, siendo $n$ el número de columnas y filas del cuadrado mágico.Si los números son consecutivos de 1 a $n^2$, la suma de los números por columnas, filas y diagonales principales es igual a : $$M_{n} = \dfrac{n(n^2+1)}{2}$$Por ejemplo, * $A= \begin{pmatrix} 4& 9 &2 \\  3&  5&7 \\  8& 1 &6 \end{pmatrix}$,es un cuadrado mágico.* $B= \begin{pmatrix} 4& 2 &9 \\  3&  5&7 \\  8& 1 &6 \end{pmatrix}$, no es un cuadrado mágico.Implemente una función llamada `es_cudrado_magico` cuyo input sea una matriz cuadrada de tamaño $n$ con números consecutivos de $1$ a $n^2$ y cuyo ouput retorne *True* si es un cuadrado mágico o 'False', en caso contrario* **Ejemplo**: *es_cudrado_magico($A$)* = True, *es_cudrado_magico($B$)* = False**Hint**: Cree una función que valide la mariz es cuadrada y  que sus números son consecutivos del 1 a $n^2$.<jupyter_code>def validar(A:np.array)->bool:
    
    """
    validar(A)

    Comprueba si la matriz A es cuadrada y que sus numeros sean consecutivos de 1 a n^2

    Parameters
    ----------
    A : np.array

    Returns
    -------
    output : bool
        True o False si la matriz cumple la validación o no.
        
    Examples
    --------
    >>> A = np.array([[1,2,3],[4,5,6],[7,8,9]])
        validar(A)
        True
        B = np.array([[1,2,3],[4,5,6]])
        validar(B)
        False
        C = np.array([[1,1,1],[4,5,6],[7,8,24]])
        validar(C)
        False
    
    """
    
    tupla = np.shape(A) #Almacenamos las dimensiones de la matriz en una tupla
    n=tupla[0]
    conjunto = set() #Definimos un conjunto para ocupar la particularidad de que no incluye numeros repetidos
    if tupla[0] == tupla[1]: #Se comprueba de inmediato si la matriz es cuadrada o no
        for i in range(0,n):
            for k in range(n): #Recorremos posición a posición la matriz
                if A[i][k] <= n*n and A[i][k]>0: #Se verifica si el el valor de la posición (i,k) es menor o igual a n^2 y si es positivo
                    conjunto.add(A[i][k])
                else:
                    return False #De inmediato se sabe que la matriz no cumple con las propiedades
        if len(conjunto) == n*n: #Como los numeros en el conjunto no se repiten basta comprobar si el largo es n^2
            return True
        else:
            return False
        
    else:
        return False
    
def es_cuadrado_magico(A:np.array)->bool:
    
    """
    es_cuadrado_magico(A)

    A partir de las condiciones de cuadrado mágico indica si la matriz es o no un cuadrado mágico

    Parameters
    ----------
    A : np.array

    Returns
    -------
    output : bool
        True o False si la matriz es o no cuadrado mágico.
        
    Examples
    --------
    >>> A = np.array([[4,2,2],[3,1,7],[8,1,6]])
        es_cuadrado_magico(A)
        False
        
    >>> B = np.array([[4,9,2],[3,5,7],[8,1,6]])
        es_cuadrado_magico(B)
        True
    """
    
    tupla=np.shape(A)
    n=tupla[0] #dimension de la matriz
    m=(n*(n**2 + 1))/2 #Formula para comprobar los cuadrados mágicos
    transpuesta = A.T
    flag = True
    if validar(A): #Validamos si la matriz es cuadrada con numeros consecutivos
        for i in range(0,n):
            if m == sum(A[i]) and sum(A[i]) == sum(transpuesta[i]) and np.trace(A) == sum(A[i]) and m == np.trace(A[::-1]): #Condiciones de un cuadrado mágico
                None #Mantenemos el flag en True mientras se hacen las iteraciones y se cumpla la condición
            else:
                flag = False
    else:
        return False
    return flag #Con flag nos aseguramos que las iteraciones se completen en todos los casos
A = np.array([[4,2,2],[3,1,7],[8,1,6]])
B = np.array([[4,9,2],[3,5,7],[8,1,6]])
print(es_cuadrado_magico(A))
print(es_cuadrado_magico(B))<jupyter_output>False
True
StarcoderdataJupyterStructuredCleanDedup