<a href="https://colab.research.google.com/github/lsantiago/PythonIntermedio/blob/master/Clases/Semana6_ALGEBRA/algebra_lineal_apuntes.ipynb" target="_parent"></a>

# Clase Nro. 6: Álgebra Lineal

> El álgebra lineal es una rama de las matemáticas que estudia conceptos tales como vectores, matrices, espacio dual, sistemas de ecuaciones lineales y en su enfoque de manera más formal, espacios vectoriales y sus transformaciones lineales

np.linalg: paquete de álgebra lineal en NumPy 

- Funciones básicas 
    - vectores
    - operaciones con vectores 
    - norma de un vector
    - operaciones con matrices
    - inversa de una matriz
    - determinante
- Resolución de sistemas


_Con el manejo básico de arrays en Python con NumPy, es hora de pasar a operaciones más interesantes como son las propias del Álgebra Lineal._

_Los productos escalares y las inversiones de matrices están por todas partes en los programas científicos e ingenieriles, así que vamos a estudiar cómo se realizan en Python._

Como sabemos, las operaciones del álgebra lineal aparecen con mucha frecuencia a la hora de resolver sistemas de ecuaciones en derivadas parciales y en general al linealizar problemas de todo tipo, y suele ser necesario resolver sistemas con un número enorme de ecuaciones e incógnitas. Gracias a los arrays de NumPy podemos abordar este tipo de cálculos en Python, ya que todas las funciones están escritas en C o Fortran y tenemos la opción de usar bibliotecas optimizadas al límite.

El paquete de álgebra lineal en NumPy se llama `linalg`, así que importando NumPy con la convención habitual podemos acceder a él escribiendo `np.linalg`. 

**Pero ¿Porqué álgebra lineal?** 

Si entendemos álgebra lineal podremos desarrollar una mejor intuición para el aprendizaje automático y algoritmos. Además, también seremos capaces de desarrollar algoritmos desde cero y hacer variaciones de ellos.

**¿Qué es un vector?**

Un vector tiene tanto magnitud como dirección. Utilizamos vectores para describir, por ejemplo, la velocidad de objetos en movimiento.

Por dirección, se refiere a dónde en el espacio apunta la “flecha”, y la magnitud te dice qué tan lejos debes ir en esa dirección. Si solo tienes magnitud, pero no dirección, entonces estás hablando de escalares. Una vez que le das al escalar alguna dirección, se convierte en un vector.

Un vector se representa con una letra minúscula y una flecha arriba, apuntando a la derecha




```python
import numpy as np
```


```python
V = np.array([3, 2])
V
```




    array([3, 2])



**Suma de vectores**

Para sumar los vectores (x₁,y₁) y (x₂,y₂), sumamos los componentes correspondientes de cada vector: (x₁+x₂,y₁+y₂)




```python
a = np.array([6, -2])
b = np.array([-4, 4])
a+b
```




    array([2, 2])



**Multiplicar un vector por un escalar**




```python
a = np.array([2, 1])
print(3 * a)
```

    [6 3]


**Producto punto o escalar**

Esto suena raro, pero no es más que la multiplicación de vectores cuyo resultado es un escalar. Para calcular el producto escalar de dos vectores, primero debemos multiplicar los elementos correspondientes y luego sumar los términos del producto.

En la siguiente fórmula lo vemos mucho más fácil






```python
a = np.array([3, 2])
b = np.array([2, 2])
print(a.dot(b))
```

    10



```python
np.dot(a, b)
```




    10



**Norma vectorial**

La norma es solo otro término para la magnitud de un vector y se denota con dos lineas dobles (||) en cada lado. Se define como una raíz cuadrada de la suma de cuadrados para cada componente de un vector

Pasos: 
- Elevar al cuadrado cada componente
- Suma todos los cuadrados
- Toma la raíz cuadrada

Trabajemos ahora con la formula:






```python
a = np.array([3, 2, 6])
print(np.linalg.norm(a))
```

    7.0


**Vector unitario**

Los vectores unitarios son aquellos cuya magnitud es exactamente 1 unidad. Son muy útiles por diversas razones. Específicamente, los vectores unitarios [0,1] y [1,0] juntos pueden formar cualquier otro vector.

Un vector unitario se denota con mayor frecuencia con un símbolo de sombrero (^) y se determina calculando la norma y luego dividiendo cada componente del vector con la norma.

Suena complejo, pero vamos a hacer un ejercicio para poder ver que es más complejo de lo que imaginamos






```python
def unit_vector(a):
    return a/np.linalg.norm(a)

u = np.array([3, 6, 4])
print(unit_vector(u))
```

    [0.38411064 0.76822128 0.51214752]


## Operaciones con matrices

 ¿Qué es una matriz? Una matriz es simplemente un arreglo rectangular de números
 
En data science las usamos un montón, así no nos demos cuenta. Es muy importante de diferenciar un vector de una matriz. En pocas palabras un vector es una sola columna (atributo) en su conjunto de datos y una matriz es una colección de todas las columnas

**Definición de matrices**

$A = \begin{equation}
\begin{bmatrix}
1 & 2 & 3\\
4 & 5 & 6\\
\end{bmatrix}
\end{equation}$


```python
A = np.array([
    [1, 2, 3], 
    [4, 5, 6]
])
A
```




    array([[1, 2, 3],
           [4, 5, 6]])



$B = \begin{equation}
\begin{bmatrix}
5 & 5 & 5\\
5 & 5 & 5\\
\end{bmatrix}
\end{equation}$


```python
B = np.full((2, 3), 5)
B
```




    array([[5, 5, 5],
           [5, 5, 5]])



$v = \begin{equation}
\begin{bmatrix}
6\\
7\\
8
\end{bmatrix}
\end{equation}$


```python
v = np.array([6, 7, 8])
v
```




    array([6, 7, 8])




```python
v.reshape(3, 1)
```




    array([[6],
           [7],
           [8]])




```python
#Generación de matriz con números aleatorios
M = np.random.rand(10).reshape(2, 5)
M
```




    array([[0.26159254, 0.35669711, 0.70822817, 0.82603537, 0.12198188],
           [0.38912962, 0.13786627, 0.48969985, 0.14139557, 0.30644094]])




```python
M[0][0]
```




    0.1869780014242599




```python
M[0, 0]
```




    0.1869780014242599




```python
M[0]
```




    array([0.186978  , 0.78673675])




```python
M[1,-1]
```




    0.3064409440254877




```python
arreglo = np.arange(1,11).reshape(2, 5)
arreglo
```




    array([[ 1,  2,  3,  4,  5],
           [ 6,  7,  8,  9, 10]])




```python
np.arange(0, 11, 3)
```




    array([0, 3, 6, 9])




```python
np.linspace(0, 20, 10)
```




    array([ 0.        ,  2.22222222,  4.44444444,  6.66666667,  8.88888889,
           11.11111111, 13.33333333, 15.55555556, 17.77777778, 20.        ])




```python
np.logspace(0, 3, 100)
```




    array([   1.        ,    1.07226722,    1.149757  ,    1.23284674,
              1.32194115,    1.41747416,    1.51991108,    1.62975083,
              1.7475284 ,    1.87381742,    2.009233  ,    2.15443469,
              2.3101297 ,    2.47707636,    2.65608778,    2.84803587,
              3.05385551,    3.27454916,    3.51119173,    3.76493581,
              4.03701726,    4.32876128,    4.64158883,    4.97702356,
              5.33669923,    5.72236766,    6.13590727,    6.57933225,
              7.05480231,    7.56463328,    8.11130831,    8.69749003,
              9.32603347,   10.        ,   10.72267222,   11.49756995,
             12.32846739,   13.21941148,   14.17474163,   15.19911083,
             16.29750835,   17.475284  ,   18.73817423,   20.09233003,
             21.5443469 ,   23.101297  ,   24.77076356,   26.56087783,
             28.48035868,   30.53855509,   32.74549163,   35.11191734,
             37.64935807,   40.37017259,   43.28761281,   46.41588834,
             49.77023564,   53.36699231,   57.22367659,   61.35907273,
             65.79332247,   70.54802311,   75.64633276,   81.11308308,
             86.97490026,   93.26033469,  100.        ,  107.2267222 ,
            114.97569954,  123.28467394,  132.19411485,  141.74741629,
            151.9911083 ,  162.97508346,  174.75284   ,  187.38174229,
            200.92330026,  215.443469  ,  231.01297001,  247.7076356 ,
            265.60877829,  284.80358684,  305.38555088,  327.45491629,
            351.11917342,  376.49358068,  403.70172586,  432.87612811,
            464.15888336,  497.70235643,  533.66992312,  572.23676594,
            613.59072734,  657.93322466,  705.48023107,  756.46332755,
            811.13083079,  869.74900262,  932.60334688, 1000.        ])




```python
#np.logspace?
```


```python
np.random.randint(1, 100)
```




    41




```python
np.random.randint(1, 100, 20).reshape(5, 4)
```




    array([[69, 47, 80, 99],
           [92, 25, 88, 61],
           [43, 20, 64, 44],
           [89, 48, 51, 34],
           [ 9, 97, 74, 17]])



**Suma de matrices**




```python
A+B
```




    array([[ 6,  7,  8],
           [ 9, 10, 11]])



**Resta de matrices**


```python
A-B
```

**Multiplicación por un escalar**




```python
C = np.array([
    [3, 5],
    [1, 0]
])
C
```




    array([[3, 5],
           [1, 0]])




```python
print(2 * C)
```

    [[ 6 10]
     [ 2  0]]


**Matriz transpuesta**




```python
D = np.array([
    [3, 4],
    [1, 0]
])
print(D)
print(D.T)
```

    [[3 4]
     [1 0]]
    [[3 1]
     [4 0]]


**Matriz identidad**


```python
I = np.identity(5)
I
```




    array([[1., 0., 0., 0., 0.],
           [0., 1., 0., 0., 0.],
           [0., 0., 1., 0., 0.],
           [0., 0., 0., 1., 0.],
           [0., 0., 0., 0., 1.]])




```python
#help(np.linalg)
```

Recordemos que si queremos usar una función de un paquete pero no queremos escribir la "ruta" completa cada vez, podemos usar la sintaxis `from package import func`:


```python
from numpy.linalg import norm, det
norm
```

El producto matricial usual (no el que se hace elemento a elemento, sino el del álgebra lineal) se calcula con la misma función que el producto matriz-vector y el producto escalar vector-vector: con la función `dot`, que **no** está en el paquete `linalg` sino directamente en `numpy` y no hace falta importarlo.


```python
np.dot
```

Una consideración importante a tener en cuenta es que en NumPy no hace falta ser estricto a la hora de manejar vectores como si fueran matrices columna, siempre que la operación sea consistente. Un vector es una matriz con una sola dimensión: por eso si calculamos su traspuesta no funciona.


```python
M = np.array([
    [1, 2],
    [3, 4]
])
v = np.array([1, -1])
```


```python
v
```




    array([ 1, -1])



v


```python
M.T
```




    array([[1, 3],
           [2, 4]])




```python
v.T
```




    array([ 1, -1])




```python
v.reshape(2,1).T
```




    array([[ 1, -1]])




```python
u = np.dot(M, v)
u
```




    array([-1, -1])



**Multiplicación entre matrices**


```python
A = np.array([
    [3, 2],
    [1, 3]
])
print(a.shape)
B = np.array([
    [2, 2],
    [1, 2]
])
print(b.shape)

#Amxn
#Bcxd

#n == c

```

    (2, 2)
    (2, 2)



```python
# producto matricial
np.dot(A, B)
```




    array([[ 8, 10],
           [ 5,  8]])



### Ejemplo de vídeo



```python
A = np.array([
    [5, 3, -4, -2],
    [8, -1, 0, -3]
    
])
B = np.array([
    [1, 4, 0],
    [-5, 3, 7],
    [0, -9, 5],
    [5, 1, 4]
])

res = np.dot(A, B)
print(res)
```

    [[-20  63  -7]
     [ -2  26 -19]]



**Determinante de una matriz**


```python
# calcula el determinante de una matriz
A = np.array([
    [3, 2],
    [1, 3]
])
```


```python
np.linalg.det(A)
```




    7.000000000000001



**Inversa de una matriz**

Para calcular la matriz inversa en Python, use la función linalg.inv () del módulo numpy.

linalg.inv(x) 

El parámetro x de la función es una matriz cuadrada invertible M definida con la función array() de numpy. 

La función genera la matriz inversa M-1 de la matriz M.

> ¿Qué es la matriz inversa? La matriz inversa M-1 de una matriz cuadrada es una matriz tal que el producto M · M-1 es igual a una matriz de identidad I.






Encuentre la matriz inversa M-1 de la siguiente matriz invertible M.




```python
M=np.array([
    [3,4,-1],
    [2,0,1],
    [1,3,-2]
])
```


```python
# Calcula la matriz inversa con la función linalg.inv().
Minv = np.linalg.inv(M)
Minv
```




    array([[-0.6,  1. ,  0.8],
           [ 1. , -1. , -1. ],
           [ 1.2, -1. , -1.6]])



La matriz de salida inversa es también un objeto array(). Se puede leer como una lista anidada. Los elementos de la matriz inversa son números reales.



Verificación. El producto de la matriz M para la matriz M-1 es una matriz de identidad.





```python
# verificación
Mident = np.dot(M, Minv)
np.round(Mident)
```




    array([[ 1., -0., -0.],
           [ 0.,  1., -0.],
           [ 0., -0.,  1.]])



**Otro ejemplo** 
Encontrar el determinante y la matriz inversa de D.

$ D = \begin{equation}
\begin{bmatrix}
1 & 2\\
3 & 4\\
\end{bmatrix}
\end{equation}$



```python
D = np.array([[1, 2], [3, 4]])
D
```




    array([[1, 2],
           [3, 4]])




```python
# cálculo del determinante
np.linalg.det(D)
```




    -2.0000000000000004




```python
# y el inverso
np.linalg.inv(D)
```




    array([[-2. ,  1. ],
           [ 1.5, -0.5]])



**Solución de ecuaciones con matrices**

1. Resolver el siguiente sistema de ecuaciones

$\begin{eqnarray}
3x + y  = 9 \\
x + 2y = 8 \\
\end{eqnarray}$


```python
coef = np.array([
    [3, 1],
    [1, 2]
])

result = np.array([9, 8])

sol = np.linalg.solve(coef, result)
sol

```




    array([2., 3.])



2. Al resolver un problema de circuito eléctrico, uno se encuentra rápidamente empantanado en
una gran cantidad de ecuaciones simultáneas. Por ejemplo, considere el circuito eléctrico que se
muestra en la figura 9.5. Contiene una sola fuente de voltaje y cinco reóstatos. Puede analizar este
circuito al dividirlo en partes más pequeñas y usar dos hechos básicos en torno a la electricidad

Sumatoria de voltaje alrededor de un circuito debe ser cero
Voltaje = corriente x resistencia (V = iR)

Seguir el lazo inferior izquierdo resulta en la primera ecuación:

$-V_1 +R_2(i_1-i_2)+R_4(i_1-i_3)=0$

Seguir el lazo superior resulta en la segunda ecuación:

$R_1i_2+R_3(i_2-i_3)+R_2(i_2-i_1)=0$

Finalmente, seguir el lazo inferior derecho resulta en la última ecuación:

$R_3(i_3-i_2)+R_5i_3+R_4(i_3-i_1)=0$

Dado que se conocen todas las resistencias (los valores R) y el voltaje, se tienen tres ecuaciones y tres incógnitas. Ahora se necesita reordenar las ecuaciones de modo que estén en una forma en la que se pueda aplicar una solución matricial. En otras palabras, se necesita aislar las íes del modo siguiente:



Cree un programa Python para resolver estas ecuaciones con el método de matriz inversa y empleando el método solve de linalg. 



1. Establezca el problema.

Encontrar las tres corrientes para el circuito que se muestra.

2. Describa las entradas y salidas.

Entrada
 Cinco resistencias R1, R2, R3, R4, R5 y el voltaje V, proporcionados desde el teclado

Salida
 Tres valores de corrientes i1, i2, i3
3. Desarrolle una solución en Python


```python
import numpy as np

R = [float(sR) for sR in input("Ingrese 5 resistencias: ").split('')]
V = float(input("Ingrese el valor del voltaje: "))

R1 = R[0]
R2 = R[1]
R3 = R[2]
R4 = R[3]
R5 = R[4]

coef = np.array([
    [(R2+R4), -R2, -R4],
    [-R2, (R1+R2+R3), -R3],
    [-R4, -R3, (R3+R4+R5)]
])

result = np.array([V, 0, 0])

# Primer método
I = np.linalg.solve(coef, result)
print(I)

# Segundo método
I = np.dot(np.linalg.inv(coef), result)
print(I)
```

    Ingrese 5 resistencias:  2 4 6 8 10
    Ingrese el valor del voltaje:  10


    [1.69354839 0.96774194 0.80645161]
    [1.69354839 0.96774194 0.80645161]


#### Lectura de una matriz


```python
NUM_ROWS = 4
a = [[int(j) for j in input().split()] for i in range(NUM_ROWS)]
```

     1 2 3 4
     4 5 6 7
     8 9 10 11
     12 13 14 15



```python
a
```




    [[1, 2, 3, 4], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]



### Ejercicios

1- Hallar el producto de estas dos matrices y su determinante:

$$\begin{pmatrix} 1 & 0 & 0 \\ 2 & 1 & 1 \\ -1 & 0 & 1 \end{pmatrix} \begin{pmatrix} 2 & 3 & -1 \\ 0 & -2 & 1 \\ 0 & 0 & 3 \end{pmatrix}$$


```python
from numpy.linalg import det
```


```python
A = np.array([
    [1, 0, 0],
    [2, 1, 1],
    [-1, 0, 1]
])
B = np.array([
    [2, 3, -1],
    [0, -2, 1],
    [0, 0, 3]
])
```


```python
C = np.dot(A, B)
```


```python
det = np.linalg.det(C)
det
```




    -12.0



3- Resuelva el siguiente sistema de ecuaciones

$\begin{eqnarray}
x + 2y  = 3 \\
3x + 4y = 5 \\
\end{eqnarray}$


```python
coef = np.array([
    [1, 2],
    [3, 4]
])

result = np.array([3, 5])

resp = np.linalg.solve(coef, result)
resp

```




    array([-1.,  2.])



4- Un ingeniereo escribe dos ecuaciones que describen el circuito, como sigue: 

$\begin{gather}
300I_{1} + 500(I_{1}-I_{2})-20 = 0\\
200I_{2} + 500(I_{2}-I_{1}) +10 = 0\\
\end{gather}$



Coloque las dos ecuaciones en forma estándar y resuelva las dos ecuaciones:


```python
# 300i1 + 500i1 - 500i2 = 20
# 200i2 + 500i2 - 500i1 = -10

#  800 i1 - 500 i2  = 20
# -500 i1 + 700 i2  = -10

coef = np.array([
    [800, -500],
    [-500, 700]
])

result = np.array([20, -10])
resp = np.linalg.solve(coef, result)
print(resp)

# Resp: [0.029, 0.0064]
```

    [0.02903226 0.00645161]


5- Resolver el siguiente sistema de ecuaciones




```python
# 2000 (i1 - i2) + 4000 (i1 - i3) - 10      = 0
# 2000 (i2 - i1) + 8000 i2 + 5000 (i2 - i3) = 0
# 5000 (i3 - i2) + 3000 i3 + 4000 (i3 - i1) = 0

# 2i1 - 2i2 + 4i1 - 4i3       = 10/1000
# 2i2 - 2i1 + 8i2 + 5i2 - 5i3 = 0
# 5i3 - 5i2 + 3i3 + 4i3 - 4i1 = 0

#  6i1 -  2i2 - 4i3 = 10/1000
# -2i1 + 15i2 - 5i3 = 0
# -4i1 - 5i2  + 12i3 = 0

coef = np.array([
    [6, -2, -4],
    [-2, 15, -5],
    [-4, -5, 12]
])

result = np.array([10/1000, 0, 0])

I = np.linalg.solve(coef, result)
I
```




    array([0.00275801, 0.00078292, 0.00124555])



3- Resolver el siguiente sistema:

$$ \begin{pmatrix} 2 & 0 & 0 \\ -1 & 1 & 0 \\ 3 & 2 & -1 \end{pmatrix} \begin{pmatrix} 1 & 1 & 1 \\ 0 & 1 & 2 \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \\ y \\ z \end{pmatrix} = \begin{pmatrix} -1 \\ 3 \\ 0 \end{pmatrix} $$


```python
M = np.dot(
np.array([
    [2, 0, 0],
    [-1, 1, 0],
    [3, 2, -1]
]),
np.array([
    [1, 1, 1],
    [0, 1, 2],
    [0, 0, 1]
]))
M
```




    array([[ 2,  2,  2],
           [-1,  0,  1],
           [ 3,  5,  6]])




```python
x = np.linalg.solve(M, np.array([-1, 3, 0]))
x
```




    array([ 0.5, -4.5,  3.5])



c91bdf8f547e5b76fb5ec3e92bf472b6df82e1e0
ipynb
Jupyter Notebook
Clases/Semana6_ALGEBRA/algebra_lineal_apuntes.ipynb
davich95/PythonIntermedio
91f444ff093662d9837829f6c9383c56565d4a42
MIT
2020-11-26T00:19:51.000Z
2020-11-26T00:19:51.000Z
Clases/Semana6_ALGEBRA/algebra_lineal_apuntes.ipynb
brstmorocho7/PythonIntermedio
3a97e8de7412c0f7e2467722b5796fea981dd321
MIT
Clases/Semana6_ALGEBRA/algebra_lineal_apuntes.ipynb
brstmorocho7/PythonIntermedio
3a97e8de7412c0f7e2467722b5796fea981dd321
MIT
2021-02-02T03:33:44.000Z
2021-02-02T03:34:20.000Z
Qwen/Qwen-72B
1. YES
2. YES