<jupyter_start><jupyter_text>MontecarloLa cópula gaussiana es una cópula construida a partir de una distribución normal multivariada. Si quieren ver las matemáticas pueden revisar wikipedia en este link).El método para generer números aleatorios correlacionados con una cópula gaussiana es el siguiente:A partir de la matriz de correlación calculamos la matriz $L$(descomposición de Cholesky).Generamos una matriz $x$ de números aleatorios independientes con distribución $N(0,1)$.Calculamos la matriz $s = x * L^T$.Aplicamos la función de distribución acumulativa normal $\phi$ a la matriz $s$, de la forma: $U = \phi(s)$.Aplicamos la función de distribución que querramos a cada columna de $U$.Ahora vamos a mostrar un ejemplo =D. Empezamos cargando una información de precios y calculamos su matriz de correlacion:<jupyter_code>import numpy as np
import scipy.stats as st
import pandas as pd

#data = pd.read_excel('Data Prices.xlsx', index_col = 0).sort_index()
data = pd.read_excel('/content/Data Prices.xlsx', index_col = 0).sort_index()
data = data.pct_change().dropna().iloc[:,[1,2,6]]

cov = np.cov(data.values.T)
mu = np.mean(data.values, axis=0)
desv = np.sqrt(np.diag(cov))
correl = np.corrcoef(data.values.T)

print("Vector de Medias Original:")
print(mu)
print("")
print("Vector de Desviacion Estandar Original:")
print(desv)
print("")
print("Matriz de Covarianza Original:")
print(cov)
print("")
print("Matriz de Correlación Original:")
print(correl)
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

def hist_scatter(x, y, bins=50):
    g = sns.JointGrid(x=x, y=y)
    g = g.plot_joint(sns.scatterplot, color="b", alpha=.6)
    _ = g.ax_marg_x.hist(x, color="r", alpha=.6,
                         bins=bins)
    _ = g.ax_marg_y.hist(y, color="green", alpha=.6,
                         orientation="horizontal",
                         bins=bins)

np.random.seed(0)
x = np.random.randn(1000, 2)
hist_scatter(x[:,0], x[:,1])
rs = np.random.RandomState(seed = 0)
n = 10000
m = data.shape[1]
x = rs.randn(n, m)
x = np.matrix(x)
L = np.linalg.cholesky(correl)

s1 = x * L.T
U1 = st.norm.cdf(s1)

x1 = st.norm.ppf(U1)
x1 = np.multiply(x1, desv) + mu

mu_1 = np.mean(x1, axis=0)
cov_1 = np.cov(x1.T)
correl_1 = np.corrcoef(x1.T)

print("Vector de Medias Original:")
print(mu)
print("")
print("Vector de Medias Simulado:")
print(mu_1)
print("")
print("Matriz de Covarianza Original:")
print(cov)
print("")
print("Matriz de Covarianza Simulada:")
print(cov_1)
print("")
print("Matriz de Correlación Original:")
print(correl)
print("")
print("Matriz de Correlación Simulada:")
print(correl_1)<jupyter_output><empty_output><jupyter_text>Se puede ver que la serie generada tiene una correlacion similar a la original, sin embargo no es igual. Si graficamos la relación de la primera con segunda columna de $x1$ se obtiene el siguiente grafico<jupyter_code>#Graficando la relacion de la primera y segunda serie simulada
hist_scatter(x1[:,0], x1[:,1])<jupyter_output><empty_output><jupyter_text>Sin embargo he descubierto un truco para que la correlación generada sea exactamente igual. Para ello se realiza lo siguiente:Luego de generar la matriz $x$ calculamos su matriz de covarianzas.Aplicamos la factorización de Cholesky sobre la matriz de covarianzas y calculamos $L1$.Calculamos la inversa de $L1$.Actualizamos $x$ con la siguiente formula: $x = x * L1^{-1}$.Repetimos este proceso como 3 veces y sobre el nuevo $x$ aplicamos la matriz $L$ del método original.Ahora vamos a ver un ejemplo =D para que vean como se hace:<jupyter_code>x = rs.randn(n, m)
x = np.matrix(x)
for i in range(0,3):
    c1 = np.cov(x.T)
    L1 = np.linalg.cholesky(c1)
    L1 = np.matrix(L1).I
    x = x * L1.T

s2 = x * L.T
U2 = st.norm.cdf(s2)
x2 = st.norm.ppf(U2)
x2 = np.multiply(x2, desv) + mu

mu_2 = np.mean(x2, axis=0)
cov_2 = np.cov(x2.T)
correl_2 = np.corrcoef(x2.T)

print("Vector de Medias Original:")
print(mu)
print("")
print("Vector de Medias Simulado con el ajuste:")
print(mu_2)
print("")
print("Matriz de Covarianza Original:")
print(cov)
print("")
print("Matriz de Covarianza Simulada con el ajuste:")
print(cov_2)
print("")
print("Matriz de Correlación Original:")
print(correl)
print("")
print("Matriz de Correlación Simulada con el ajuste:")
print(correl_2)<jupyter_output><empty_output><jupyter_text>Ahora podran ver que ambas matrices son identicas, con esto se mejora la precision de la simulación. Si graficamos la relación de la primera con segunda columna de $x2$ se obtiene el siguiente grafico<jupyter_code>hist_scatter(x2[:,0], x2[:,1])<jupyter_output><empty_output><jupyter_text>Ahora para demostrar que podemos generar cualquier tipo de distribución correlacionada, usando la simulación anterior aplicaremos las inversas acumulativas de las distribuciones normal, t y uniforme para calcular las nuevas distribuciones correlacionadas.<jupyter_code>x3 = st.norm.ppf(U2)
x3[:,0] = st.norm.ppf(U2[:,0], loc=0.5, scale=0.3)
x3[:,1] = st.t.ppf(U2[:,1], df = n - 1)
x3[:,2] = st.uniform.ppf(U2[:,2])

correl_3 = np.corrcoef(x3.T)

print("Matriz de Correlación Original:")
print(correl)
print("")
print("Matriz de Correlación Simulada con el ajuste:")
print(correl_3)
hist_scatter(x3[:,0], x3[:,1])<jupyter_output><empty_output><jupyter_text>Graficando la relacion de la serie con distribucion normal y uniforme<jupyter_code>hist_scatter(x3[:,0], x3[:,2])<jupyter_output><empty_output><jupyter_text>Y con eso pueden ver que se han generados muestras de distintas distribuciones correlacionadas. B. Cópula t-Student.La cópula t-Student es una cópula construida a partir de una distribución t-Student multivariada. Si quieren ver las matemáticas pueden revisar el paper de este link.El método para generer números aleatorios correlacionados con una cópula t-Student es el siguiente:A partir de la matriz de correlación calculamos la matriz $L$(descomposición de Cholesky).Calculamos los grados de libertad con la fórmua $\nu = (n-1)*d$, donde $d$ es el numero de dimensiones y $n$ el número de observaciones a simular.Generamos un vector $V$ de dimension de números aleatorios independientes con distribución $\chi(\nu)$.Generamos una matriz $x$ de números aleatorios independientes con distribución $N(0,1)$.Calculamos la matriz $s = \sqrt{\frac{\nu}{V}} * (x * L^T)$.Aplicamos la función de distribución acumulativa t-Student $t_{\nu}$ a la matriz $s$, de la forma: $U = t_{\nu}(s)$.Aplicamos la función de distribución que querramos a cada columna de $U$.Ahora vamos a mostrar un ejemplo =D de la simulación con la cópula t-Student:<jupyter_code>x = rs.randn(n, m)
x = np.matrix(x)

v = (n - 1) * m
V = rs.chisquare(v, size = (n, 1))

s4 = np.sqrt(v/V) * np.array(x * L.T)
U4 = st.t.cdf(s4, v)

x4 = st.t.ppf(U4, v)
#x4[:,0] = st.norm.ppf(U4[:,0])
#x4[:,1] = st.t.ppf(U4[:,1], df = 2)
#x4[:,2] = st.uniform.ppf(U4[:,2])

correl_4 = np.corrcoef(x4.T)

print("Matriz de Correlación Original:")
print(correl)
print("")
print("Matriz de Correlación Simulada sin el ajuste:")
print(correl_4)<jupyter_output><empty_output><jupyter_text>Se aprecia que al igual que para las copulas gaussianas, la aproximación no es muy buena. Si aplicamos el truco de anterior<jupyter_code>x = rs.randn(n, m)
x = np.matrix(np.sqrt(v/V) * np.array(x))

for i in range(0,3):
    c1 = np.cov(x.T)
    L1 = np.linalg.cholesky(c1)
    L1 = np.matrix(L1).I
    x = x * L1.T

s5 = x * L.T
U5 = st.t.cdf(s5, v)

x5 = st.t.ppf(U5, v)

correl_5 = np.corrcoef(x5.T)

print("Matriz de Correlación Original:")
print(correl)
print("")
print("Matriz de Correlación Simulada con el ajuste:")
print(correl_5)<jupyter_output><empty_output><jupyter_text>Ahora pueden ver que la correlacion ha mejorado comparado a la simulación anterior.Usando la simulación anterior aplicaremos las inversas acumulativas de las distribuciones normal, t y uniforme para calcular las nuevas distribuciones correlacionadas.<jupyter_code>x6 = st.t.ppf(U5, v)
x6[:,0] = st.norm.ppf(U5[:,0])
x6[:,1] = st.t.ppf(U5[:,1], df = n - 1)
x6[:,2] = st.uniform.ppf(U5[:,2])

correl_6 = np.corrcoef(x6.T)

print("Matriz de Correlación Original:")
print(correl)
print("")
print("Matriz de Correlación Simulada con el ajuste:")
print(correl_6)
hist_scatter(x6[:,0], x6[:,2])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1312605855310912, 'fraction_numerical': 0.021654972175175417, 'mean_word_length': 5.119170984455959, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '459424', 'license': 'MIT', 'n_tokens_mistral': 3270, 'n_tokens_neox': 3238, 'n_words': 912, 'path': 'MOntecarlo_parte1_copula.ipynb', 'repo_name': 'WuilsonEstacio/github-para-estadistica', 'size': '8266'}