<jupyter_start><jupyter_text>**Estatistica descritiva básica**<jupyter_code>import seaborn as sns
sns.set_palette("Set1", 8, .75)
import matplotlib.pyplot as plt

import numpy as np
import pandas as pd
from src.intro_desc_stats import prepare_data, display_side_by_side<jupyter_output><empty_output><jupyter_text>**TOC:**- [Tipo de variáveis](variables_types)---- [Probabilidade](proba)    - [Variáveis Aleatórias](random_var)        - [Espaço amostral](espaco_amostral)        - [Conjuntos](conjuntos)        - [Definição de Probabilidade](def_prob)---- [Distribuição de Probabilidade](dist_proba)    - [Distribuição Uniforme](dist_uni)    - [Distribuição Gaussiana](dist_gaus)---- [Estatística Descritiva](desc_stat)    - [Medidas de Tendência Central](tendencia_central)    - [Medidas de Dispersão](medidas_dispersao)---- [Outliers](outliers)    - [Z score](z_score)    - [Boxplot whisker (IQR)](iqr_outlier)  **Tipos de variáveis**  * **Nominal**: Escala de medição em que os números servem apenas como rótulos e não indicam nenhuma relação quantitativa. Exemplos: sexo, estado civil, país de origem, ser fumante ou não, etc.* **Ordinais**: Escala de medição em que os números são postos; diferenças iguais entre números não representam diferenças iguais entre as coisas medidas. Exemplo: escolaridade, resposta de um paciente(piora, igual, melhora), classe social(alta, média,baixa)* **Interval**: Escala de medição na qual diferenças iguais entre números representam diferenças iguais na coisa medida. O ponto zero é definido arbitrariamente.* **Ratio Variables**: Escala de medição com características de escala intervalar, mas possui um *ponto zero verdadeiro*. \begin{array}{lcccl}\hline \text{Tipo de variavel}  & \text{transmite} \leq & \text{transmite} = & \text{Possuí zero verdadeiro} & \text{Exemplo}\\\hline       \text{Nominal ou Categorical}               & Não                   & Não                & Não  & \text{Diagnóstico psicólogico, Tipos de Personalidade}\\       \text{Ordinal ou Ranked}               & Sim                  & Não                & Não  & \text{Posição de chegada de uma corrida}\\       \text{Interval}              & Sim                  & Sim               & Não  & \text{Celsius, Teste de QI}\\       \text{Ratio}                 & Sim                  & Sim               & Sim & \text{Altura, Peso}\\\hline\end{array} Diferença entre variáveis ***Interval*** e ***Ratio***:- Converta $ 100 ^\circ C$ e $ 50 ^\circ C$ para Fahrenheit (F = 1,8C + 32) e de repente a relação "duas vezes mais" desaparece.- Converta 16 kg e 4 kg para libras (1 kg = 2,2 lbs) e a relação "quatro vezes mais pesado" é mantido.A diferença é sutil. Portanto, essas variáveis são referenciadas como variáveis contínuas.  **Probabilidade**   Probabilidade é a área da matemática que estuda eventos aleatórios, cujos resultados são probabilísticos ou estocásticos.Existem poucos sistemas na natureza que são de fato inerentemente aleatórias e não-determinísticas. No entanto, uma descrição determinística pode ser muito complicada, de modo que se torna conveniente tratar o sistema como um sistema estocástico.Exemplos: lanaçamento de moeda, lançamento de dados, etc.O objetivo da estatística é similar: analisar dados ou criar modelos que generalizem relações que não podem ser determinadas diretamente uma vez que não temos acesso à população. Neste sentido, há muita probabilidade envolvida em estatística, e é por isso que vamos olhar para os principais conceitos de probabilidade.  **Variável aleatória**  Variáveis aleatórias são normalmente denotadas por uma letra capital como **X**.**Mais formalmente:** Uma função que mapeia um "espaço de probabilidades" na reta real $\mathbb{R}$.**Menos formal:** Uma função que mapeia o desfecho de um processo aleatório (Exemplo: jogar uma moeda) para números reais (probabilidades).   **Espaço amostral**   Chamamos de **espaço amostral** o conjunto de **todos os resultados possíveis de um certo fenômeno aleatório**, ou seja, os valores possíveis que a **variável aleatória de interesse pode assumir**. Representamos o espaço amostral pela letra grega $\omega$. Os subconjuntos de $\omega$ são denominados **eventos** e representados pelas letras maiúsculas A, B, etc...**Exemplo:** Qual o espaço amostral dos resultadosdo lançamento de um dado?$$\omega = \{1,2,3,4,5,6\}$$Alguns eventos possíveis são:- Evento A = \{3\}: o resultado do lançamento do dado foi 3;- Evento B = \{5\}: o resultado do lançamento do dado foi 5; **Exercicio 1:** Qual o espaço amostral do resultado do lançamento de uma moeda? **Exercicio 2:** Qual o espaço amostral do resultado do lançamento de duas moedas?  **Conjuntos**<jupyter_code>A = {2,4,6}
B = {2,3,5}<jupyter_output><empty_output><jupyter_text>- O conjunto vazio é denotado por $\emptyset$ e equivale a um conjunto sem nenhum elemento.- A união de dois eventos A e B, denotado por $\cup$ representa a ocorrência de pelo menos um dos eventos A ou B.- A intersecção do evento A com B, denotado por $\cap$ é a ocorrência simultânea de A e B.<jupyter_code>print(A.union(B))
print(A.intersection(B))<jupyter_output>{2, 3, 4, 5, 6}
{2}<jupyter_text>Dois eventos A e B são disjuntos ou mutualmente exclusivos quando não têm elemento em comum. Isto é, A $\cap$ B = $\emptyset$.<jupyter_code>X = {1,2,3}
Y = {4,5,6}
print(X.intersection(Y))<jupyter_output>set()<jupyter_text>Dois eventos A e B são complementares se sua união é o espaço amostral e sua intersecção é vazia. O complementar de A será representado por $A^c$<jupyter_code>U = {1,2,3,4,5,6}

cond1 = X.union(Y) == U
cond2 = X.intersection(Y) == set()

if cond1 and cond2:
    print(r'X e Y são complementares!')
else:
    print(r'X e Y não são complementares!')<jupyter_output>X e Y são complementares!<jupyter_text>**Definição de probabilidade**  Definimos uma função $P(.)$ denominada probabilidade que deve satisfazer as seguintes propriedades:1. $0 \leq P(A) \leq 1 $2. $P(\omega) = 1$3. $P(\cup A_j) = \sum_{j=1}^{n}P(A_j) $ **Exemplo:** Suponha o lançamento de uma moeda. Temos $\omega = \{K, C\}$. 1. Qualquer evento $A$ deve ter uma probabilidade entre 0 e 1.2. A probabilidade de lançarmos uma moeda e ter um desfecho que esteja compreendido em $\omega$ é 1.3. Seja $\{K\} = A_1$ e $\{C\} = A_2$ temos $P(A_1 \cup A_2)= P(A_1) + P(A_2)$  **Distribuições de probabilidades**  Uma distribuição de probabilidade é uma função que descreve o comportamento aleatório de um fenômeno dependente do acaso.A distribuição de probabilidade pode modelar incertezas e descrever fenômenos físicos, biológicos, econômicos, entre outros.Para representar a distribuição matematicamente, frequentemente utilizamos a [função densidade de probabilidade](https://en.wikipedia.org/wiki/Probability_density_function). Esta função é tal que a probabilidade do desfecho da variável aleatória cair em uma faixa particular é dada pela integral da densidade dessa variável sobre tal faixa - isto é, é dada pela área abaixo da função densidade mas acima do eixo horizontal e entre o menor e o maior valor dessa faixa.   **Distribuição uniforme**<jupyter_code>n_samples = 100000
x = np.random.uniform(0, 1, size=n_samples)

fig, axs = plt.subplots(1,3,figsize=(16,4))

def plot_distribution(func, x, ax, title, **kwargs):
    g = func(x, ax=ax, **kwargs)
    g.set_title(title)
    
plot_distribution(sns.histplot, x, axs[0], "Histograma", stat="density")
plot_distribution(sns.kdeplot, x, axs[1], "Kernel density Estimation - KDE")
plot_distribution(sns.ecdfplot, x, axs[2], "Empirical cumulative distribution function - ECDF")

plt.tight_layout()
plt.show()
p=0.5
resultados = np.where(np.random.uniform(0, 1, 1000) <= p, "K", "C")
pd.Series(resultados).value_counts(normalize=True)<jupyter_output><empty_output><jupyter_text>**Distribuição Gaussiana**  É representada por uma curva simétrica em torno do seu ponto médio, apresentando assim seu famoso formato de sino (bell shape).A curva de distribuição normal representa o comportamento de diversos processos e fenômenos comuns, como por exemplo: altura ou peso de uma população, a pressão sanguínea de um grupo de pessoas, o tempo que um grupo de estudantes gasta para realizar uma prova, etc.A distribuição normal serve também como base para a inferência estatística clássica, sendo a premissa em diversos modelos e métodos.Na distribuição normal, a média, mediana e moda dos dados possuem o mesmo valor.A fórmula da da distribuição gaussiana é:$$f(x) = \frac{1}{\sigma\sqrt{2\pi}}e^{\frac{-(x-\mu)^2}{2\sigma^2}}$$onde $\mu$ é a média e $\sigma$ é o desvio padrão.<jupyter_code>n_samples = 100000
x = np.random.normal(loc=0.0, scale=1.0, size=n_samples)

fig, axs = plt.subplots(1,3,figsize=(16,4))

def plot_distribution(func, x, ax, title, **kwargs):
    g = func(x, ax=ax, **kwargs)
    g.set_title(title)
    
plot_distribution(sns.histplot, x, axs[0], "Histograma", stat="density")
plot_distribution(sns.kdeplot, x, axs[1], "Kernel density Estimation - KDE")
plot_distribution(sns.ecdfplot, x, axs[2], "Empirical cumulative distribution function - ECDF")

plt.tight_layout()
plt.show()<jupyter_output><empty_output><jupyter_text>Na distribuição normal, os dados são distribuídos de forma muito particular, tal que o **desvio padrão** determina a proporção de dados em cada faixa: Exemplo: Fumantes pagam mais que não fumantes?<jupyter_code>df = sns.load_dataset("tips")
n_samples = len(df)
var = "tip"
mean, std = df.loc[:, var].mean(), df.loc[:, "tip"].std()

x = np.random.normal(loc=mean, scale=std, size=n_samples)

fig, axs = plt.subplots(1,3,figsize=(16,4))

def plot_distribution(func, x, ax, title, **kwargs):
    g = func(x, ax=ax, **kwargs)
    g.set_title(title)
    ax.legend()
    
kwargs_estimado = {"color": "r",
                   "label": "Estimado"}

kwargs_original = {"color": "k",
                   "label": "Original"}
plot_distribution(sns.histplot, x, axs[0], "Histograma", stat="density", **kwargs_estimado)
plot_distribution(sns.kdeplot, x, axs[1], "Kernel density Estimation - KDE", **kwargs_estimado)
plot_distribution(sns.ecdfplot, x, axs[2], "Empirical cumulative distribution function - ECDF", **kwargs_estimado)


x_original = df[var].values
plot_distribution(sns.histplot, x_original, axs[0], "Histograma", stat="density", **kwargs_original)
plot_distribution(sns.kdeplot, x_original, axs[1], "Kernel density Estimation - KDE", **kwargs_original)
plot_distribution(sns.ecdfplot, x_original, axs[2], "Empirical cumulative distribution function - ECDF", **kwargs_original)

plt.suptitle(f"Variavel: {var}", fontsize = 18)
plt.tight_layout()
plt.show()
n_samples = len(df)
var = "total_bill"
mean, std = df.loc[:, var].mean(), df.loc[:, "tip"].std()

x = np.random.normal(loc=mean, scale=std, size=n_samples)

fig, axs = plt.subplots(1,3,figsize=(16,4))

def plot_distribution(func, x, ax, title, **kwargs):
    g = func(x, ax=ax, **kwargs)
    g.set_title(title)
    ax.legend()
    
kwargs_estimado = {"color": "r",
                   "label": "Estimado"}

kwargs_original = {"color": "k",
                   "label": "Original"}
plot_distribution(sns.histplot, x, axs[0], "Histograma", stat="density", **kwargs_estimado)
plot_distribution(sns.kdeplot, x, axs[1], "Kernel density Estimation - KDE", **kwargs_estimado)
plot_distribution(sns.ecdfplot, x, axs[2], "Empirical cumulative distribution function - ECDF", **kwargs_estimado)


x_original = df[var].values
plot_distribution(sns.histplot, x_original, axs[0], "Histograma", stat="density", **kwargs_original)
plot_distribution(sns.kdeplot, x_original, axs[1], "Kernel density Estimation - KDE", **kwargs_original)
plot_distribution(sns.ecdfplot, x_original, axs[2], "Empirical cumulative distribution function - ECDF", **kwargs_original)

plt.suptitle(f"Variavel: {var}", fontsize = 18)
plt.tight_layout()
plt.show()<jupyter_output><empty_output><jupyter_text>**Estatistica Descritiva**   Como mencionamos, o principal objetivo da **estatística descritiva** é gerar medidas que resumam o conjunto de dados que estamos analisando, ou seja, medidas que descrevam a distribuição dos dados, de forma quantitativa. Vamos dar uma olhada nessas medidas!   **Medidas de tendencia central**   **Média**  Seja $X$ uma variável quantitativa e $x_0,x_1,x_2, ...$ os valores assumidos por X. Define-se média de $\overline{X}$ como sendo :$$\overline{X} = \frac{\sum_{i=0}^{n-1}x_i}{n}= \frac{x_0+ x_1 + x_2 +... +x_n}{n}$$<jupyter_code>df["tip"].mean()<jupyter_output><empty_output><jupyter_text>**Mediana**  A **mediana** de uma variável é um número tal que há o mesmo número de observações maiores e menores do que ele, ocupando assim a **posição central** da série de observações.Assim, para calcularmos a mediana, basta **ordenarmos os dados, do menor pro maior**, e contar o que está no meio dos dados ordenados (se houver número par de dados, pega-se a média entre os dados que estão "no meio")- *Exemplo*i) 3,4,**7**,8,8 **mediana=7**       (CASO ÍMPAR)ii) 3,4,**7**,**8**,8,9 **mediana=(7+8)/2=7,5**   (CASO PAR)Logo, podemos definir - - Mediana de $X = X_{\frac{n}{2}}$ `Se tamanho da amostra par`- - Mediana de $X = \frac{X_{\frac{n-1}{2}}+ X_{\frac{n}{2}}}{2}$ `Se tamanho da amostra ímpar`<jupyter_code>df["tip"].median()<jupyter_output><empty_output><jupyter_text>**Quartis** Quartis (Q1, Q2 e Q3): São valores dados a partir do conjunto de observações ordenado em ordem crescente, que dividem os dados **em quatro partes iguais**. - O primeiro quartil, Q1, é o número que deixa 25% das observações abaixo e 75% acima;- O segundo quartil, Q2 é a **mediana**, deixa 50% das observações abaixo e 50% das observações acima;- O terceiro quartil, Q3, deixa 75% das observações abaixo e 25% acima;<jupyter_code>df["tip"].quantile(0.25), df["tip"].quantile(0.5), df["tip"].quantile(0.75)<jupyter_output><empty_output><jupyter_text>**Moda** A **moda** é o valor que ocorre com **maior frequencia** em um conjunto de dados Dependendo do conjunto de dados, ele pode ser:- Sem moda: quando nenhum valor se repete - unimodal: Quando existe apenas um valor repetido com maior frequência - multimodal: quando existem mais de um valor com a mesma frequência máxima<jupyter_code>df["tip"].mode()<jupyter_output><empty_output><jupyter_text>**Medidas de dispersão**   **Intervalo Interquartílico (IQR - interquantile range)**  Por construção, aproximadamente metade dos dados estão entre o 1º e o 3º quartil, isto é, este também pode ser interpretado como **um intervalo de valores tipicamente assumidos pelas observações**, e também é uma **medida de dispersão**, pois nos dá uma ideia da variação que existe entre o Q1 e o Q3!<jupyter_code>df["tip"].quantile(0.75) - df["tip"].quantile(0.25)<jupyter_output><empty_output><jupyter_text>**Amplitude** A amplitude é a **diferença entre o maior e o menor valor observado**. - **Fortemente influenciada** por valores extremos nas observações, como outliers.<jupyter_code>df["tip"].max() - df["tip"].min()<jupyter_output><empty_output><jupyter_text>**Variância** A variância representa **o quanto os dados se afastam da média, em geral**. Por definição, tomamos a média dos **desvios quadráticos da média**:$$ Var(X) = \sum_{i=1}^{n}\frac{(x-\overline{x})^{2}}{n}$$<jupyter_code>df["tip"].var()<jupyter_output><empty_output><jupyter_text>**Desvio padrão** Note que, da forma como foi definida, a variância é um valor **quadrático** nos dados. Para evitar isso, é comum tirar a raiz quadrada da variância, o que define o **desvio padrão**:$$ \sigma(X) \equiv \sqrt{Var(X)} = \sqrt{\sum_{i=1}^{n}\frac{(x-\overline{x})^{2}}{n}}$$<jupyter_code>df["tip"].std(), np.sqrt(df["tip"].var())<jupyter_output><empty_output><jupyter_text>**Assimetria - skewness** É o **grau de desvio ou afastamento** da **simetria** de uma distribuição.Quando a curva é simétrica, a `média`, a `mediana` e a `moda` coincidem, num mesmo ponto, havendo um perfeito equilíbrio na distribuição. Um exemplo de distribuição simétrica é a **distribuição normal**.Quando o equilíbrio não acontece, a média, a mediana e a moda recaem em pontos diferentes da distribuição, e a distribuição se torna **assimétrica**:- Distribuição assimétrica **Negativa ou enviesada a esquerda**: quando os valores se concentram na extremidade superior da escala e se distribuem gradativamente em direção à extremidade inferior.- Distribuição assimétrica **Positiva ou enviesada a direita**: quando os valores se concentram na extremidade inferior da escala e se distribuem gradativamente em direção à extremidade superior.A medidade de assimetria é conhecida como **skewness**, sendo calculada como:$$ s(X) = \frac{1}{\sigma^3}\sum_{i=1}^{n}\frac{(x-\overline{x})^{3}}{n}$$O valor da skewness nos indica a assimetria da distribuição:- s = 0: distribuição simétrica- s > 0: assimetria à direita- s < 0: assimetria à esquerda<jupyter_code>df["tip"].skew()<jupyter_output><empty_output><jupyter_text>**Outliers** **Outliers** ou **discrepantes** referem-se a obsevações **extremas**, que se deslocam em muito das outras observações em um conjunto de dados, ou seja, que **destoam do padrão da distribuição** dos dados.Outliers podem existir por motivos diferentes, alguns dos quais são:- erro na entrada de dados (erro humano);- erro na medição/coleta dos dados (erro instrumental);- erros experimentais (planejamento/execução dos experimentos);- outliers naturais (não decorrente de erros)No caso dos erros, é importante que os outliers sejam tratados (comumente são removidos).Por outro lado, outliers naturais são importantes constituintes dos dados, de modo que não devem ser eliminados -- mas, muitas vezes, devem ser tratados.De qualquer modo, antes de começarmos a pensar em como **tratar** os outliers, o primeiro passo é **identifica-los**. Para discussões interessantes sobre outliers, como identificá-los e tratá-los, dê uma olhada [neste post](https://towardsdatascience.com/5-ways-to-detect-outliers-that-every-data-scientist-should-know-python-code-70a54335a623) ou [neste](https://towardsdatascience.com/a-brief-overview-of-outlier-detection-techniques-1e0b2c19e561).---  **Z-score** O **z-score** de uma observação nada mais é que **o número de desvios padrões que a obervação está com relação à média**:$$ z = \frac{x - \mu}{\sigma} $$$$ z \sigma = x - \mu $$Como vimos acima, em uma distribuição normal, 99.73% das observações são esperadas de estar entre 3 desvios-padrão da média. Assim, obervações para além desta faixa são muito improváveis de ocorrer (0.27% de chance), de forma que podem ser consideradas outliers.Assim, é comum considerar outliers os pontos tais que $|z| > 3$ (embora seja comum também variar este z-score limite, para 2.5 o, 3.5, etc.)**OBS.: naturalmente, este método é altamente dependente da hipótese de que seus dados são normalmente distribuídos! Por isso, funciona apenas aproximadamente para outros casos. Mas é uma boa aproximação, em geral.**<jupyter_code>df_wh = pd.read_csv('data/weight-height.csv')
# passando altura de polegadas pra centimetros
df_wh["Height"] = df_wh["Height"] * 2.54
# passando peso de libras pra quilos
df_wh["Weight"] = df_wh["Weight"] / 2.2046

df_wh<jupyter_output><empty_output><jupyter_text>As variáveis numéricas parecem estar normalmente distribuídas?<jupyter_code>display(df_wh.describe())

print("Parece que sim, pois a média e mediana são bem próximas!")

male = df_wh[df_wh["Gender"] == "Male"].copy()
female = df_wh[df_wh["Gender"] == "Female"].copy()

male["z_height"] = male["Height"].apply(lambda x: (x-male["Height"].mean())/male["Height"].std())
male["z_weight"] = male["Weight"].apply(lambda x: (x-male["Weight"].mean())/male["Weight"].std())

female["z_height"] = female["Height"].apply(lambda x: (x-female["Height"].mean())/female["Height"].std())
female["z_weight"] = female["Weight"].apply(lambda x: (x-female["Weight"].mean())/female["Weight"].std())
print("Outliers homens apenas para a altura:")
display(male[male["z_height"].abs() > 3])

print("\nOutliers homens apenas para o peso:")
display(male[male["z_weight"].abs() > 3])

print("\n ############################################### \n")

print("Outliers mulheres apenas para a altura:")
display(female[female["z_height"].abs() > 3])

print("\nOutliers mulheres apenas para o peso:")
display(female[female["z_weight"].abs() > 3])<jupyter_output>Outliers homens apenas para a altura:<jupyter_text>**Boxplot whisker (IQR)** Neste método, outliers são identificados como **os pontos que estão para além dos whiskers em um boxplot**.Se você estiver interessado em saber porque os whiskers são definidios comumente com este fator 1.5 no "1.5\*IQR", dê uma olhada [neste post!](https://towardsdatascience.com/why-1-5-in-iqr-method-of-outlier-detection-5d07fdc82097)<jupyter_code># calcule o IQR das variáveis numericas dos dois dataframes

q1_mh = male["Height"].quantile(0.25)
q3_mh = male["Height"].quantile(0.75)
iqr_mh = q3_mh - q1_mh

q1_mw = male["Weight"].quantile(0.25)
q3_mw = male["Weight"].quantile(0.75)
iqr_mw = q3_mw - q1_mw

print("IQR homens altura:", iqr_mh, "\tIQR homens peso:", iqr_mw)

print("\n############################################\n")

q1_fh = female["Height"].quantile(0.25)
q3_fh = female["Height"].quantile(0.75)
iqr_fh = q3_fh - q1_fh

q1_fw = female["Weight"].quantile(0.25)
q3_fw = female["Weight"].quantile(0.75)
iqr_fw = q3_fw - q1_fw

print("IQR mulheres altura:", iqr_fh, "\tIQR mulheres peso:", iqr_fw)
# calcule os whiskers das variaveis numericas dos dois datraframes

max_mh = q3_mh + 1.5*iqr_mh
min_mh = q1_mh - 1.5*iqr_mh

max_mw = q3_mw + 1.5*iqr_mw
min_mw = q1_mw - 1.5*iqr_mw

print("Whiskers homens altura:", min_mh, max_mh)
print("Whiskers homens peso:", min_mw, max_mw)


max_fh = q3_fh + 1.5*iqr_fh
min_fh = q1_fh - 1.5*iqr_fh

max_fw = q3_fw + 1.5*iqr_fw
min_fw = q1_fw - 1.5*iqr_fw

print("Whiskers mulheres altura:", min_fh, max_fh)
print("Whiskers mulheres peso:", min_fw, max_fw)<jupyter_output>Whiskers homens altura: 156.09209884343895 194.84299482267338
Whiskers homens peso: 60.86488053730561 108.89179601908499
Whiskers mulheres altura: 143.23252010330015 180.5108168421164
Whiskers mulheres peso: 38.15606053097511 85.1066730422688
{'fraction_non_alphanumeric': 0.1302813715733285, 'fraction_numerical': 0.021758798516239936, 'mean_word_length': 5.192436974789916, 'pattern_counts': {'":': 8, '<': 78, '<?xml version=': 0, '>': 82, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '20', 'dir': 'jupyter-structured-clean-dedup', 'id': '123073', 'license': 'MIT', 'n_tokens_mistral': 8368, 'n_tokens_neox': 7821, 'n_words': 2394, 'path': 'semana_4/intro_estatistica_descritiva_gabarito.ipynb', 'repo_name': 'rocabrera/curso_ml', 'size': '22106'}