<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># importar libreria: numpy
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),mean(4,5)] = [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`  Definir Función<jupyter_code>def sma(a:np.array,n:int)->np.array:
    """
    sma(a,n)

    Cálculo de la media móvil sobre el arreglo a, con ventana n.

    Parameters
    ----------
    a : np.array
        Arreglo al cual se le calculará la media móvil
        
    n : int
        Ventana móvil de la SMA 
    Returns
    -------
    output : np.array
        Arreglo con las medias moviles de a 
        
    Examples
    --------
    >>> sma(np.array([1,2,3,4,5]),n=2)
    array([1.5, 2.5, 3.5, 4.5])
    
    >>> sma(np.array([1,2,3,4,5]),n=2)
    array([1.5, 2.5, 3.5, 4.5])
    """
    prom=[]                            #Definición del arreglo de salida
    for i in range((len(a)-(n-1))):    #iteración solo sobre los primeros len(a)-(n-1) términos pues no es necesario iterar más
        prom.append(np.mean(a[i:i+n])) #Se agrega el promedio simple de los n elementos agrupados
    return np.array(prom)              #Se retorna el arreglo construido<jupyter_output><empty_output><jupyter_text>Verificar Ejemplos<jupyter_code># ejemplo 01
a = np.array([1,2,3,4,5])
np.testing.assert_array_equal(
    sma(a, n=2),
    np.array([1.5, 2.5, 3.5, 4.5])
)
# ejemplo 02
a =np.array([5,3,8,10,2,1,5,1,0,2])

np.testing.assert_array_equal(
    sma(a, n=2),
    np.array([4. , 5.5, 9. , 6. , 1.5, 3. , 3. , 0.5, 1. ])
)<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}$  Definir Función<jupyter_code>def strides(a:np.array,n:int,p:int)->np.array:
    """
    strides(a,n,p)

    Construcción de matriz desfasada de n columnas con paso p a partir de a

    Parameters
    ----------
    a : np.array
        Matriz de la cual construir la matriz desfasada
    n : int
        Número de columnas de la matriz desfasada
    p : int 
        paso del desface de la matriz a construir
        
    Returns
    -------
    output : np.array
        Matriz desfasada de n columnas y de paso p
        
    Examples
    --------
    >>> strides(np.array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10]),4,2)
    array([[ 1.,  2.,  3.,  4.],
           [ 3.,  4.,  5.,  6.],
           [ 5.,  6.,  7.,  8.],
           [ 7.,  8.,  9., 10.]])
    
    >>> strides(np.array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10]),4,3)
    array([[ 1.,  2.,  3.,  4.],
           [ 4.,  5.,  6.,  7.],
           [ 7.,  8.,  9., 10.]])
    """
    fil=int((len(a)-n)/p + 1) #Se calcula el número de filas que debe tener la matriz desfasada
    mat=np.zeros((fil,n))     #Se crea una matriz de fil filas y n columnas en donde ir construyendo la matriz deseada
    for i in range(0,fil):    #Se itera sobre las filas de mat
        mat[i,:]=a[i*p:n+i*p] #Se construyen las filas siguiendo el criterio de desface
    return mat                #Se retorna la matriz desfasada<jupyter_output><empty_output><jupyter_text>Verificar Ejemplos<jupyter_code># ejemplo 01
a = np.array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
n=4
p=2

np.testing.assert_array_equal(
    strides(a,n,p),
    np.array([
       [ 1,  2,  3,  4],
       [ 3,  4,  5,  6],
       [ 5,  6,  7,  8],
       [ 7,  8,  9, 10]])
)
# ejemplo 02
a = np.array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
n=4
p=3
np.testing.assert_array_equal(
    strides(a,n,p),
    np.array([[ 1.,  2.,  3.,  4.],
              [ 4.,  5.,  6.,  7.],
              [ 7.,  8.,  9., 10.]])
)<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$.  Definir Función<jupyter_code>def validar(A:np.array)->bool:
    """
    validar(A)

    Validación que la matriz A es cuadrada y solo contiene números consecutivos 

    Parameters
    ----------
    A : np.array
        Matriz a validar

    Returns
    -------
    output : bool
        Valor de verdad de la proposición: "A es una matriz cuadrada y solo contiene numeros consecutivos del 1 al n**2"
        
    Examples
    --------
    >>> validar(np.array([[4,9,2],
                          [3,5,7],
                          [8,1,6]])
                )
    True
    >>> validar(np.array([[5,9,2],
                          [3,5,7],
                          [8,1,6]])
                )
    False
    """
    cjto=set()                         #Definición de conjunto vacío 
    if np.shape(A)[0]==np.shape(A)[1]: #Chequeo de si la matriz es cuadrada
        n=np.shape(A)[0]**2            #Definición de variable que almacena el tamaño al cuadrado de la matriz 
        for i in A:                    #iteración sobre las filas de A
            for j in i:                #iteración sobre las columnas de A
                if j <= n and j > 0:   #Verificación de que el elemento j pertenece al conjunto {1,2,...,n**2}
                    cjto.add(j)        #Construcción del conjunto a verificar igualdad con {1,2,...,n**2}
        if len(cjto) == n:             #Si el cjto tiene la misma cantidad de elementos que #{1,2,...,n**2} entonces son iguales
            return True                #Como los cjtos son iguales, se retorna True
        else:
            return False               #Si cjto no tiene la misma cantidad, se retorna False
    else:
        return False                   #Si la matriz no era cuadrada en un principio, se retorna Falso 
def es_cudrado_magico(A:np.array)->bool:
    """
    es_cudrado_magico(A)

    Verificación de si A es un cuadrado mágico

    Parameters
    ----------
    A : np.array
        Matriz a verificar 

    Returns
    -------
    output : bool
        Valor de verdad de la proposición: "A es un cuadrado mágico"
        
    Examples
    --------
    >>> es_cudrado_magico(np.array([[2,9,4],
                                    [3,5,7],
                                    [8,1,6]])
                          )
    False
    
    >>> es_cudrado_magico(np.array([[17,24,1,8,15],
                                    [23,5,7,14,16],
                                    [4,6,13,20,22],
                                    [10,12,19,21,3],
                                    [11,18,25,2,9]])
                          )
    True
    """
    if validar(A):                   #Se chequea si la matriz es cuadrada y solo contiene numeros consecutivos del 1 al n**2
        n=np.shape(A)[0]
        M_n=n*(n**2 +1)/2            #Definición de la ctte mágica
        if np.trace(A)!=M_n:         #Chequeo de si la traza=ctte mágica
            return False
        if np.trace(A[::-1]) != M_n: #Chequeo de si la traza secundaria=ctte mágica
            return False
        for i in range(n):           #iteración para chequear la suma de las filas y columnas
            if np.sum(A[i])!= M_n:   #Chequeo de suma de filas =ctte mágica
                return False
            if np.sum(A[:,i]) != M_n:#Chequeo de suma de columnas =ctte mágica
                return False
        return True                  #Si la matriz pasa todos los chequeos, es un cuadrado mágico y se retorna True
    else:
        return False                 #Si no, se retorna False<jupyter_output><empty_output><jupyter_text>Verificar Ejemplos<jupyter_code># ejemplo 01
A = np.array([[4,9,2],[3,5,7],[8,1,6]])
assert es_cudrado_magico(A) == True, "ejemplo 01 incorrecto"
# ejemplo 02
B = np.array([[4,2,9],[3,5,7],[8,1,6]])
assert es_cudrado_magico(B) == False, "ejemplo 02 incorrecto"
#Profe acá le dejo algunas matrices que usé para probar mi función:

A=np.array([        #Este si es cuadrado mágico
    [4,9,2],
    [3,5,7],
    [8,1,6]
])
B=np.array([        #Este no es cuadrado mágico
    [2,9,4],
    [3,5,7],
    [8,1,6]
])
C=np.array([        #Este si es cuadrado mágico
    [5,9,2],
    [3,5,7],
    [8,1,6]
])
D=np.array([        #Este si es cuadrado mágico
[17,24,1,8,15],
[23,5,7,14,16],
[4,6,13,20,22],
[10,12,19,21,3],
[11,18,25,2,9],
])
E=np.array([         #Este si es cuadrado mágico
[30,39,48,1,10,19,28],
[38,47,7,9,18,27,29],
[46,6,8,17,26,35,37],
[5,14,16,25,34,36,45],
[13,15,24,33,42,44,4],
[21,23,32,41,43,3,12],
[22,31,40,49,2,11,20]
])
F=np.array([          #Este no es cuadrado mágico
[30,39,48,1,10,19,28],
[38,47,7,9,18,27,29],
[46,6,8,17,26,35,37],
[5,14,16,25,33,36,45],
[13,15,24,33,42,44,4],
[21,23,32,41,43,3,12],
[22,31,40,49,2,11,20]
])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15470048097944905, 'fraction_numerical': 0.06331438565806734, 'mean_word_length': 2.2824339839265213, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 58, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '407037', 'license': 'MIT', 'n_tokens_mistral': 4816, 'n_tokens_neox': 4528, 'n_words': 1276, 'path': 'labs/02_numpy/laboratorio_02.ipynb', 'repo_name': 'Khris-VI/Portafolio-mat281-Cristobal-Vivar', 'size': '11435'}