<jupyter_start><jupyter_text>Aprendizaje Supervisado con Python: Regresión----  Agenda1- Introducción* Comunidad de Analítica y temas tratados en el 2017* Recursos en Github* Aprendizaje supervisado* Regresión lineal clásica* Regresión lineal regularizada    + LASSO    + Ridge    + Elastic Net  2- Caso de estudio y exploración de los datos * Caso de estudio* Pregunta y problemática de negocio* Contenido y estructura de los datos3- Procesamiento y limpieza de los datos* Limpieza preliminar* Tratamiento de valores extremos* Normalización de la variable objetivo  4- Ingeniería de características* Imputación de valores perdidos* Transformación de variables* Codificación de etiquetas (clases)* Normalización* Variables ficticias (dummies)  5- Modelado, evaluación y predicción* Regresión lineal clásica* Regresión lineal regularizada    + LASSO    + Ridge    + Elastic Net  Comunidad de Analítica y temas tratados en el 2017[Nuestra Comunidad](https://www.meetup.com/Comunidad-de-Analitica-en-Santo-Domingo-R-Python/)  [Votación: Temas de Interés en Ciencia de Datos con Python](https://www.meetup.com/Comunidad-de-Analitica-en-Santo-Domingo-R-Python/polls/1252269/)    Recursos en Github[PyRDR](https://github.com/pyrdr/charlas)  Aprendizaje Automático Supervisado$$\Huge Y = f(X, \varepsilon)$$![Algoritmos de Machine Learning](figs/machine_learning_methods.png)  Regresión![](figs/regresion_lineal_simple.png)  Regresión lineal clásica (estándar o gaussiana)$$ $$$$\begin{align} \Large E(Y \hspace{0.1cm} \text{|} \hspace{0.1cm} X_i) = \beta_{0} + \beta_{1} X_{1i} + \beta_{2} X_{2i} ... + \beta_{k} X_{ki} + \varepsilon_i\end{align}$$$$ $$$$\Large \varepsilon_i \sim \large N$$ Supuestos:* S01: Lineal en sus parámetros* S02: Los regresores $X$ son fijos en muetras repetidas, es decir, no-estocásticos* S03: Los disturbios tiene valor medio cero $E(\varepsilon_i | X_i) = 0$* S04: Homocedasticidad: dados los valores de $X$, la varianza de los disturbios $\varepsilon_i$ es la misma para todas las observaciones: $$var(\varepsilon_i \hspace{0.1cm} \text{|} \hspace{0.1cm} X_i) = E[\varepsilon_i − E(\varepsilon_i \hspace{0.1cm} \text{|} \hspace{0.1cm} X_i)^2] = E(\varepsilon_i^2 \hspace{0.1cm} \text{|} \hspace{0.1cm} X_i ) = \sigma^2$$* S05: No existe autocorrelación entre los disturbios. Dados dos valores de $X$, $X_i$ y $X_j$ ($i \neq j$), la correlación entre cualquier par $\varepsilon_i$ y $\varepsilon_j$ ($i \neq j$) es cero. * S07: El número de observaciones $n$ debe ser más grande que el número de parámetros estimados.* S08: Variabilidad en los valores de $X$: $var(X)$ debe ser un número positivo finito.* S09: El modelo de regresión está correctamente especificado, es decir, no existe sesgo de especificación.* S10: No existe multicolinearidad perfecta, es decir, no existen relaciones lineales perfectas entre las variables explicativas.* S11: Para fines de inferencia, los disturbios siguen una distribución gaussiana ó normal ($\varepsilon_i \sim N$). * Regresión lineal clásica* Regresión lineal regularizada    + LASSO    + Ridge    + Elastic Net  https://www.analyticsvidhya.com/blog/2017/06/a-comprehensive-guide-for-linear-ridge-and-lasso-regression/  Caso de estudio y exploración de los datos * Caso de estudio (Kaggle)       + https://www.kaggle.com/c/house-prices-advanced-regression-techniques/data     + https://www.kaggle.com/juliencs/a-study-on-regression-applied-to-the-ames-dataset     + https://www.kaggle.com/serigne/stacked-regressions-top-4-on-leaderboard  * ¿Cuál es la pregunta de negocio que debemos responder? ¿cuál es la problemática de negocios que debemos resolver?<jupyter_code># Librerías
import pandas as pd
import numpy as np
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, RidgeCV, LassoCV, ElasticNetCV
from sklearn.metrics import mean_squared_error, make_scorer
from scipy import stats
from scipy.stats import norm, skew
from IPython.display import display
import matplotlib.pyplot as plt
import seaborn as sns

# Opciones
pd.set_option('display.float_format', lambda x: '%.3f' % x)
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Contenido y estructura de los datos<jupyter_code># Para verificar los archivos contenidos en el directorio
from subprocess import check_output
print(check_output(["ls", "../python_regression/"]).decode("utf8"))
# Para leer los datos
train = pd.read_csv("train.csv")
print("train : " + str(train.shape))

test = pd.read_csv('test.csv')
print("test : " + str(test.shape))
## Despliega las primeras cinco filas del conjunto de datos de entrenamiento
train.head(5)
## Describe los datos de entrenamiento
train.describe()
# Verifica el número de observaciones y atributos
print("The train data size before dropping Id feature is : {} ".format(train.shape))
print("The test data size before dropping Id feature is : {} ".format(test.shape))

# Guarda la columna 'Id'
train_ID = train['Id']
test_ID = test['Id']

# Ahora prescinde de la columna 'Id', ya que es innecesaria para el proceso de predicción
train.drop("Id", axis = 1, inplace = True)
test.drop("Id", axis = 1, inplace = True)

# Verifica de nuevo el tamaño de los datos después de eliminar la variable 'Id'
print("\nThe train data size after dropping Id feature is : {} ".format(train.shape)) 
print("The test data size after dropping Id feature is : {} ".format(test.shape))<jupyter_output>The train data size before dropping Id feature is : (1460, 81) 
The test data size before dropping Id feature is : (1459, 80) 

The train data size after dropping Id feature is : (1460, 80) 
The test data size after dropping Id feature is : (1459, 79)<jupyter_text>Procesamiento de datos Valores extremos (outliers)La [documentación](http://ww2.amstat.org/publications/jse/v19n3/Decock/DataDocumentation.txt) de los datos de Ames Housing indica que existen valores extremos (outliers) en los datos de entrenamiento.Vamos a explorar estos valores.<jupyter_code>fig, ax = plt.subplots()
ax.scatter(x = train['GrLivArea'], y = train['SalePrice'])
plt.ylabel('SalePrice', fontsize=13)
plt.xlabel('GrLivArea', fontsize=13)
plt.show()
# Eliminamos los outliers
train = train.drop(train[(train['GrLivArea']>4000) & (train['SalePrice']<300000)].index)

# Verificamos de nuevo el gráfico
fig, ax = plt.subplots()
ax.scatter(train['GrLivArea'], train['SalePrice'])
plt.ylabel('SalePrice', fontsize=13)
plt.xlabel('GrLivArea', fontsize=13)
plt.show()<jupyter_output><empty_output><jupyter_text>Nota :Eliminar los valores extremos (outliers) no es siempre lo más apropiado. En nuestro caso procedimos a eliminar estos dos casos, debido a que eran valores muy elevados y que no hacían sentido (áreas extremadamente grandes a precios muy bajos).Puede que existan otros valores extremos en los datos de entrenamiento. Sin embargo, removerlos todos puede afectar de mala manera nuestro ejercicio de modelado si también encontramos outliers en los datos de comprobación (test). Por eso, en lugar de removerlos todos, preferimos solo manejar aquellos casos que hagan nuestros modelos más robustos.  Variable Objetivo (Target)**SalePrice** (el precio de venta) es la variable que necesitamos predecir. Por eso realizamos algunos análisis preliminares sobre dicha variable.<jupyter_code>sns.distplot(train['SalePrice'] , fit=norm);

# Obtenemos los parámetros de ajuste utilizados por la función
(mu, sigma) = norm.fit(train['SalePrice'])
print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma))

# Graficamos la distribución empírica
plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)],
            loc='best')
plt.ylabel('Frequency')
plt.title('SalePrice distribution')

# QQ-plot para comprobar la normalidad
fig = plt.figure()
res = stats.probplot(train['SalePrice'], plot=plt)
plt.show()<jupyter_output>mu = 180932.92 and sigma = 79467.79<jupyter_text>Transformación logarítmica de la variable objetivo<jupyter_code># Empleamos la función log1p de numpy para obtener el log(1+x) de todos los elementos de la variable objetivo
train["SalePrice"] = np.log1p(train["SalePrice"])

# Verificamos la nueva distribución empírica 
sns.distplot(train['SalePrice'] , fit=norm);

# Nuevos parámetros
(mu, sigma) = norm.fit(train['SalePrice'])
print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma))

# Nuevo gráfico después de la transformación
plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)],
            loc='best')
plt.ylabel('Frequency')
plt.title('SalePrice distribution')

# QQ-plot después de la transformación
fig = plt.figure()
res = stats.probplot(train['SalePrice'], plot=plt)
plt.show()

# Registramos la nueva variable target como y_train
y_train = train.SalePrice.values<jupyter_output>mu = 12.02 and sigma = 0.40<jupyter_text>Ingeniería de atributos (Features engineering)Vamos primero a concatenar los dos data frames (train y test) a fin de simplicar el trabajo.<jupyter_code>ntrain = train.shape[0]
ntest = test.shape[0]
all_data = pd.concat((train, test)).reset_index(drop=True)
all_data.drop(['SalePrice'], axis=1, inplace=True)
print("all_data size is : {}".format(all_data.shape))<jupyter_output>all_data size is : (2917, 79)<jupyter_text>Valores perdidos (Missing data)<jupyter_code>all_data_na = (all_data.isnull().sum() / len(all_data)) * 100
all_data_na = all_data_na.drop(all_data_na[all_data_na == 0].index).sort_values(ascending=False)[:30]
missing_data = pd.DataFrame({'Missing Ratio' :all_data_na})
missing_data.head(30)
f, ax = plt.subplots(figsize=(15, 12))
plt.xticks(rotation='0')
sns.barplot(x=all_data_na, y=all_data_na.index)
plt.xlabel('Features', fontsize=15)
plt.ylabel('Percent of missing values', fontsize=15)
plt.title('Percent missing data by feature', fontsize=15)<jupyter_output><empty_output><jupyter_text>Imputamos los valores perdidos según cada caso **PoolQC** : la descripción de datos dice que NA signifca "No Pool". Esto hace sentido, dado el enorme porcentaje de valores perdidos (+99%), y el hecho de que, en general, la mayoría de las viviendas no tienen piscina.<jupyter_code>all_data["PoolQC"] = all_data["PoolQC"].fillna("None")<jupyter_output><empty_output><jupyter_text>Casos similares.<jupyter_code>all_data["MiscFeature"] = all_data["MiscFeature"].fillna("None")
all_data["Alley"] = all_data["Alley"].fillna("None")
all_data["Fence"] = all_data["Fence"].fillna("None")
all_data["FireplaceQu"] = all_data["FireplaceQu"].fillna("None")

for col in ('GarageType', 'GarageFinish', 'GarageQual', 'GarageCond'):
    all_data[col] = all_data[col].fillna('None')<jupyter_output><empty_output><jupyter_text>**LotFrontage** : Dado que el área de cada calle conectada a la casa, muy probablemente es similar para las otras casas del vecindario, podemos rellenar los valores perdidos con la mediana de LotFrontage del vecindario.<jupyter_code># Agrupamos por vecindario y completamos los valores perdidos con la mediana de LotFrontage para todos los vecindarios
all_data["LotFrontage"] = all_data.groupby("Neighborhood")["LotFrontage"].transform(
    lambda x: x.fillna(x.median()))<jupyter_output><empty_output><jupyter_text>**GarageYrBlt**, **GarageArea** y **GarageCars** : Reemplazamos los datos perdidos con cero, ya que "no garage" equivale a que no hay carros en dicho garage.<jupyter_code>for col in ('GarageYrBlt', 'GarageArea', 'GarageCars'):
    all_data[col] = all_data[col].fillna(0)<jupyter_output><empty_output><jupyter_text>**BsmtFinSF1**, **BsmtFinSF2**, **BsmtUnfSF**, **TotalBsmtSF**, **BsmtFullBath**, **BsmtHalfBath** : los valores perdidos muy probablemente son cero por no tener basement.<jupyter_code>for col in ('BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF','TotalBsmtSF', 'BsmtFullBath', 'BsmtHalfBath'):
    all_data[col] = all_data[col].fillna(0)<jupyter_output><empty_output><jupyter_text>**BsmtQual**, **BsmtCond**, **BsmtExposure**, **BsmtFinType1**, **BsmtFinType2** : Para todos estos atributos categóricos relacionados con el basement, NaN significa que no hay basement.<jupyter_code>for col in ('BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2'):
    all_data[col] = all_data[col].fillna('None')<jupyter_output><empty_output><jupyter_text>**MasVnrArea** y **MasVnrType** : NA muy probablemente indica que estas casas no tienen una chapa de albañilería. De manera que podemos reemplazar con cero el área y con None el tipo.<jupyter_code>all_data["MasVnrType"] = all_data["MasVnrType"].fillna("None")
all_data["MasVnrArea"] = all_data["MasVnrArea"].fillna(0)<jupyter_output><empty_output><jupyter_text>**MSZoning** (clasificación general de zonificación) : 'RL' es por mucho el valor más común. De manera que podemos reemplazar los valores perdidos con 'RL'.<jupyter_code>all_data['MSZoning'] = all_data['MSZoning'].fillna(all_data['MSZoning'].mode()[0])<jupyter_output><empty_output><jupyter_text>**Utilities** : Para este atributo categórico todos los registros son "AllPub", excepto por uno "NoSeWa" y 2 NA. Dado que la casa con 'NoSewa' está en el juego de datos de entrenamiento, este atributo no ayudará en el modelado predictivo. Por tanto, podemos eliminarlo sin problema.<jupyter_code>all_data = all_data.drop(['Utilities'], axis=1)<jupyter_output><empty_output><jupyter_text>**Functional**: data description says NA means typical<jupyter_code>all_data["Functional"] = all_data["Functional"].fillna("Typ")<jupyter_output><empty_output><jupyter_text>**Electrical** : Tiene un valor NA. Pero este atributo en su mayoría es 'SBrkr', de manera que podemos reemplazar el valor perdido por 'SBrkr'.<jupyter_code>all_data['Electrical'] = all_data['Electrical'].fillna(all_data['Electrical'].mode()[0])<jupyter_output><empty_output><jupyter_text>**KitchenQual**: Sólo un valor NA, lo sustituimos por 'TA' (el más frecuente).<jupyter_code>all_data['KitchenQual'] = all_data['KitchenQual'].fillna(all_data['KitchenQual'].mode()[0])<jupyter_output><empty_output><jupyter_text>**Exterior1st** y **Exterior2nd** : De nuevo, Exterior 1 & 2 tienen un sólo valor perdido. Sustituimos por el más común.<jupyter_code>all_data['Exterior1st'] = all_data['Exterior1st'].fillna(all_data['Exterior1st'].mode()[0])
all_data['Exterior2nd'] = all_data['Exterior2nd'].fillna(all_data['Exterior2nd'].mode()[0])<jupyter_output><empty_output><jupyter_text>**SaleType** : Rellenamos con el más frecuente ("WD")<jupyter_code>all_data['SaleType'] = all_data['SaleType'].fillna(all_data['SaleType'].mode()[0])<jupyter_output><empty_output><jupyter_text>**MSSubClass** : Na muy probablemente indica clase "no building", por lo que reemplazamos con None<jupyter_code>all_data['MSSubClass'] = all_data['MSSubClass'].fillna("None")<jupyter_output><empty_output><jupyter_text>**Quedan aún valores perdidos?**<jupyter_code># Verificamos si permanecen aún valores perdidos
all_data_na = (all_data.isnull().sum() / len(all_data)) * 100
all_data_na = all_data_na.drop(all_data_na[all_data_na == 0].index).sort_values(ascending=False)
missing_data = pd.DataFrame({'Missing Ratio' :all_data_na})
missing_data.head()<jupyter_output><empty_output><jupyter_text>Ya no quedan valores perdidos.  Más ingeniería de atributos: Transformando algunas variables numéricas que realmente son categóricas<jupyter_code># MSSubClass = El tipo de edificios
all_data['MSSubClass'] = all_data['MSSubClass'].apply(str)


# OverallCond
all_data['OverallCond'] = all_data['OverallCond'].astype(str)


# Año y mes de venta.
all_data['YrSold'] = all_data['YrSold'].astype(str)
all_data['MoSold'] = all_data['MoSold'].astype(str)<jupyter_output><empty_output><jupyter_text>Codifica en cifras los valores nominales de algunas variables categóricas, para un mejor desempeñohttps://www.analyticsvidhya.com/blog/2015/11/easy-methods-deal-categorical-variables-predictive-modeling/<jupyter_code>from sklearn.preprocessing import LabelEncoder

cols = ('FireplaceQu', 'BsmtQual', 'BsmtCond', 'GarageQual', 'GarageCond', 
        'ExterQual', 'ExterCond','HeatingQC', 'PoolQC', 'KitchenQual', 'BsmtFinType1', 
        'BsmtFinType2', 'Functional', 'Fence', 'BsmtExposure', 'GarageFinish', 'LandSlope',
        'LotShape', 'PavedDrive', 'Street', 'Alley', 'CentralAir', 'MSSubClass', 'OverallCond', 
        'YrSold', 'MoSold')

# procesa columnas, applicando LabelEncoder a los atributos categóricos
for c in cols:
    lbl = LabelEncoder() 
    lbl.fit(list(all_data[c].values)) 
    all_data[c] = lbl.transform(list(all_data[c].values))

# Shape        
print('Shape all_data: {}'.format(all_data.shape))<jupyter_output>Shape all_data: (2917, 78)<jupyter_text>Adicionamos un atributo muy importanteLos atributos relacionados con el área (pies cuadrados, metros cuadrados) son muy importante para determinar los precios de las casas. Vamos a añadir un atributo que equivale al área combinada del basement, más el primer piso y el segundo piso de cada casa.<jupyter_code># Adicionamos el total de pies cuadrados (TotalSF) de la vivienda
all_data['TotalSF'] = all_data['TotalBsmtSF'] + all_data['1stFlrSF'] + all_data['2ndFlrSF']<jupyter_output><empty_output><jupyter_text>Atributos sesgados<jupyter_code>numeric_feats = all_data.dtypes[all_data.dtypes != "object"].index

# Verificamos el sesgo de todos los atributos numéricos
skewed_feats = all_data[numeric_feats].apply(lambda x: skew(x.dropna())).sort_values(ascending=False)
print("\nSkew in numerical features: \n")
skewness = pd.DataFrame({'Skew' :skewed_feats})
skewness.head(20)<jupyter_output>Skew in numerical features:<jupyter_text>Transformación Box-Cox de atributos (altamente) sesgados.Mediante la función $\text{boxcox1p}$ calculamos la transformación Box-Cox de $1 + x$. Debemos notar que configurar   $\lambda = 0$ es equivalente a $\text{loglp}$ usado más arriba para la variable target.  * http://onlinestatbook.com/2/transformations/box-cox.html  * https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.special.boxcox1p.html<jupyter_code>skewness = skewness[abs(skewness) > 0.75]
print("There are {} skewed numerical features to Box Cox transform".format(skewness.shape[0]))

from scipy.special import boxcox1p
skewed_features = skewness.index
lam = 0.15
for feat in skewed_features:
    #all_data[feat] += 1
    all_data[feat] = boxcox1p(all_data[feat], lam)
    
# all_data[skewed_features] = np.log1p(all_data[skewed_features])<jupyter_output>There are 59 skewed numerical features to Box Cox transform<jupyter_text>Variables ficticias (dummies)<jupyter_code>all_data = pd.get_dummies(all_data)
print(all_data.shape)<jupyter_output>(2917, 220)<jupyter_text>Nuevos conjuntos de datos de entrenamiento y comprobación.<jupyter_code>train = all_data[:ntrain]
test = all_data[ntrain:]<jupyter_output><empty_output><jupyter_text>Correlación de datos<jupyter_code># Mapa para detectar qué atributos están correlacionados con el precio de venta
corrmat = train.corr()
plt.subplots(figsize=(12,9))
sns.heatmap(corrmat, vmax=0.9, square=True)<jupyter_output><empty_output><jupyter_text>Modelado<jupyter_code>from sklearn.linear_model import LinearRegression, ElasticNet, Lasso, BayesianRidge, LassoLarsIC
from sklearn.kernel_ridge import KernelRidge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import RobustScaler
from sklearn.base import BaseEstimator, TransformerMixin, RegressorMixin, clone
from sklearn.model_selection import KFold, cross_val_score, train_test_split
from sklearn.metrics import mean_squared_error<jupyter_output><empty_output><jupyter_text>Estrategia de validación cruzada (cross-validation)Emplearemos la función $\text{cross_val_score}$ de Sklearn. Sin embargo, esta función no tiene un atributo de mezcla aleatoria de datos. De manera que adicionamos previamente una línea de código en orden a realizar dicha mezcla.<jupyter_code># Función de validación cruzada con mezcla aleatoria previa
n_folds = 5

def rmsle_cv(model):
    kf = KFold(n_folds, shuffle=True, random_state=42).get_n_splits(train.values)
    rmse= np.sqrt(-cross_val_score(model, train.values, y_train, scoring="neg_mean_squared_error", cv = kf))
    return(rmse)<jupyter_output><empty_output><jupyter_text>ModelosAquellos modelos que pueden ser muy sensibles a los valores extremos (outliers), los vamos robustecer utilizando el método Robustscaler() de Sklearn en el pipeline. **Regresión lineal (clásica)**:<jupyter_code>REG = make_pipeline(RobustScaler(), LinearRegression())<jupyter_output><empty_output><jupyter_text>**Regresión LASSO**:<jupyter_code>LASSO = make_pipeline(RobustScaler(), Lasso(alpha =0.0005, random_state=1))<jupyter_output><empty_output><jupyter_text>**Regresión Elastic Net**:<jupyter_code>ENet = make_pipeline(RobustScaler(), ElasticNet(alpha=0.0005, l1_ratio=.9, random_state=3))<jupyter_output><empty_output><jupyter_text>**Regresión con Kernel Ridge**:<jupyter_code>KRR = KernelRidge(alpha=0.6, kernel='polynomial', degree=2, coef0=2.5)<jupyter_output><empty_output><jupyter_text>Scores de los modelosVeamos ahora cómo estos modelos se desempeñan sobre los datos evaluando la métrica [RMSLE](https://www.quora.com/What-is-the-difference-between-an-RMSE-and-RMSLE-logarithmic-error-and-does-a-high-RMSE-imply-low-RMSLE).[Función de Costo RMSLE](docs/rmslecostfunction-160623032122.pdf)<jupyter_code>score = rmsle_cv(REG)
print("Linear regression score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))

score = rmsle_cv(LASSO)
print("Lasso score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))

score = rmsle_cv(ENet)
print("ElasticNet score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))

score = rmsle_cv(KRR)
print("Kernel Ridge score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))

# Ajustamos el modelo con el mejor score a los datos y predecimos los valores
LASSO.fit(train, y_train)
y_pred = LASSO.predict(test)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12550885056945524, 'fraction_numerical': 0.014362164387321045, 'mean_word_length': 6.666199158485274, 'pattern_counts': {'":': 0, '<': 146, '<?xml version=': 0, '>': 147, 'https://': 10, 'lorem ipsum': 0, 'www.': 8, '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': '38', 'dir': 'jupyter-structured-clean-dedup', 'id': '317790', 'license': 'MIT', 'n_tokens_mistral': 8114, 'n_tokens_neox': 7795, 'n_words': 1918, 'path': 'python_regression/python_regression.ipynb', 'repo_name': 'pyrdr/charlas', 'size': '21863'}