Sumário

  • 1  Distribuições de Frequência
  • 2  Medidas de Tendência Central
    • 2.1  Média Aritmética
    • 2.2  Média Ponderada
    • 2.3  Media Geométrica
    • 2.4  Mediana
    • 2.5  Moda
  • 3  Medidas de dispersão
    • 3.1  Desvio Médio Absoluto
    • 3.2  Variância
    • 3.3  Desvio Padrão
  • 4  Quantis
  • 5  Box Plot
  • 6  Assimetria
  • 7  Curtose
  • 8  Transformações
  • 9  Referências
In [1]:
import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import pyspark.sql.functions as F
In [2]:
df = pd.read_csv('dados/dados_tratados.csv').drop('Unnamed: 0', axis=1)
df_spark = spark.read.format("csv").option("header","true").load('dados/dados_tratados.csv')
df_spark.createOrReplaceTempView('df_sql')
df.head()
Out[2]:
ano_trabalho nivel_experiencia tipo_de_contrato cargo salario moeda_salario salario_em_dolares pais_residencia tipo_trabalho local_companhia tamanho_companhia
0 2023 Senior/Especialista Tempo integral Principal Data Scientist 80000 EUR 85847 ES Home office ES Grande
1 2023 Pleno Contrato ML Engineer 30000 USD 30000 US Home office US Pequeno
2 2023 Pleno Contrato ML Engineer 25500 USD 25500 US Home office US Pequeno
3 2023 Senior/Especialista Tempo integral Data Scientist 175000 USD 175000 CA Home office CA Medio
4 2023 Senior/Especialista Tempo integral Data Scientist 120000 USD 120000 CA Home office CA Medio

Distribuições de Frequência¶

Uma distribuição de frequência é uma tabela que devolve todos os valores com suas respectivas frequências.

Exemplo: Numa empresa há 5 funcionários com os seguintes salários (em R$): 2000,00; 5030,00; 2588,00; 4005,00; 2900,00

Salário (em R$) Frequência observada Frequência Relativa (\%) Frequência Acumulada
2000,00 ⊢ 3000,00 3 60 60
3000,00 ⊢ 4000,00 0 60 60
4000,00 ⊢ 5000,00 1 20 80
Acima de 5000,00 1 20 100
Total 5 100

Onde:

  • Salário: É a classe observada.
  • Frequência observada: Quantidade de elementos em cada classe. Também pode ser indicado por $n_i$
  • Frequência relativa: É a porcentagem de valores da classe em relação ao total de observações dos dados. Também pode ser indicado por $f_i$
  • Frequência acumulada: É a soma das frequências relativas até aquela classe. Também pode ser indicado por $f_{ac}$

OBS.:

⊢ significa que o intervalo compreende o valor da esquerda, mas não o da direita

Ex.: 2000,00 ⊢ 3000,00: Salários iguais ou maiores que 2000,00 e menores que 3000,00

In [3]:
tabela_frequencia = df[['nivel_experiencia', 'salario']].groupby('nivel_experiencia').agg({'salario': 'count'})
tabela_frequencia.columns = ['frequencia_observada']
tabela_frequencia['frequencia_relativa'] = tabela_frequencia['frequencia_observada'] / tabela_frequencia['frequencia_observada'].sum()
tabela_frequencia["frequencia_acumulada"] = tabela_frequencia["frequencia_relativa"].cumsum()
tabela_frequencia
Out[3]:
frequencia_observada frequencia_relativa frequencia_acumulada
nivel_experiencia
Diretor 114 0.030360 0.030360
Junior 320 0.085220 0.115579
Pleno 805 0.214381 0.329960
Senior/Especialista 2516 0.670040 1.000000

Medidas de Tendência Central¶

Média Aritmética¶

Média Aritmética é a soma das observações dividida pela quantidade de observações.

$$\overline{x} = \frac{x_1 + x_2 + \cdot\cdot\cdot + x_n}{n} = \frac{1}{n}\sum_{i = 1}^{n}x_i$$

Essa medida de tendência central não é útil se na amostra existirem valores discrepantes (outliers)

Exemplo: Temos na mesma equipe 4 funcionários, com salários de 5000,00 reais; 4000,00 reais; 4500,00 reais; e 25000,00 reais.

A média é: $$\overline{x} = \frac{5000 + 4000 + 4500 + 25000}{4} = \text{R\$} 9625,00$$

Esse salário médio é puxado pra cima por causa do salário discrepante de R$ 25.000,00.

Vamos ver como usar essa medida no Python.

In [4]:
media = (5000 + 4000 + 4500 + 25000) / 4
media
Out[4]:
9625.0
In [5]:
# criando uma função na mão para calcular media
def media(valores):
    return sum(valores) / len(valores)
In [6]:
lista = [5000, 4000, 4500, 25000]
media(lista)
Out[6]:
9625.0
In [7]:
# para o nosso conjunto de dados
media(df['salario'])
Out[7]:
190695.57177097205
In [8]:
# com pandas
df['salario'].mean()
Out[8]:
190695.57177097205
In [9]:
# com pyspark
df_spark.select(F.mean('salario')).show()
+------------------+
|      avg(salario)|
+------------------+
|190695.57177097205|
+------------------+

In [10]:
# no pyspark, uma forma de melhorar o nome da coluna que sai é usando o comando alias
df_spark.select(F.mean('salario').alias('media do salário')).show()
+------------------+
|  media do salário|
+------------------+
|190695.57177097205|
+------------------+

In [11]:
# com sql
spark.sql('SELECT AVG(SALARIO) AS MEDIA_SALARIO FROM df_sql').show()
+------------------+
|     MEDIA_SALARIO|
+------------------+
|190695.57177097205|
+------------------+

Média Ponderada¶

A média ponderada é uma média que recebe um valor que será multiplicado para cada observação, chamado de peso, que dará um determinado nível de importância para a observação.

Sua fórmula é: $$\overline{x}_p = \frac{\sum_{i = 1}^{n} \omega_i x_i}{\sum_{i = 1}^{n} \omega_i}$$

onde $\omega$ é o peso da observação.

Exemplo: Uma empresa quer saber qual sua média salarial. Sabe-se que há três cargos na empresa, que são:

Cargo Quantidade de funcionário Salario
Assistente Administrativo 12 2500,00
Contador 5 5000,00
Gerente 1 8000,00

Qual a média salarial da empresa:

$$\overline{x}_p = \frac{(12 \cdot 2500) + (5 \cdot 5000) + (1 \cdot 8000}{12 + 5 + 1} = \frac{63000}{18} = \text{R\$}3500,00$$

O salário médio da empresa é R$ 3500,00

Vamos ver como ficaria no Python

In [12]:
df_media_ponderada = (df[['nivel_experiencia', 'salario_em_dolares', 'salario']]
                      .groupby('nivel_experiencia')
                      .agg({'salario_em_dolares': 'mean',
                            'salario': 'count'})
                      .reset_index())

df_media_ponderada.columns = ['nivel_experiencia', 'media_salario', 'quantidade']
df_media_ponderada_spark = spark.createDataFrame(df_media_ponderada) 
df_media_ponderada_spark.createOrReplaceTempView('df_media_ponderada_sql')
In [13]:
# na mão
def media_ponderada(df, valores, pesos):
    return sum(df[valores] * df[pesos]) / df[pesos].sum()

media_ponderada(df_media_ponderada, 'media_salario', 'quantidade')
Out[13]:
137570.38988015978
In [14]:
# com numpy 
np.average(a=df_media_ponderada['media_salario'],
           weights=df_media_ponderada['quantidade'])
Out[14]:
137570.38988015978
In [15]:
# com spark
df_media_ponderada_spark.select(F.expr('SUM(media_salario * quantidade) / SUM(quantidade)').alias('media_ponderada')).show()
+------------------+
|   media_ponderada|
+------------------+
|137570.38988015978|
+------------------+

                                                                                
In [16]:
# com sql
spark.sql('SELECT SUM(media_salario * quantidade) / SUM(quantidade) AS media_ponderada FROM df_media_ponderada_sql').show()
+------------------+
|   media_ponderada|
+------------------+
|137570.38988015978|
+------------------+

Media Geométrica¶

A média geométrica é definida, para o conjunto de números positivos, como a raiz $n$-ésima do produto de $n$ elementos de um conjunto de dados. A propriedade principal dessa média é preservar o produto dos elementos de um conjunto de dados.

Sua fórmula é: $$G = (\prod^{n}_{i=1}x_i)^{1/n} =\sqrt[n]{x_1 x_2 \cdot \cdot \cdot x_n }$$

Uma das principais aplicações da média geométrica é nos cálculos que englobam taxas de crescimento (proporcionais, variados e exponenciais), ou seja, valores que passam por sucessivos aumentos ou permanecem de forma contínua.

Exemplo: O preço do kg do arroz sofreu aumentos sucessivos nos últimos 4 meses de 10%, 15%, 8% e 40%. Qual foi o aumento médio percentual nesse período?

$$G = \sqrt[n]{x_1 x_2 \cdot \cdot \cdot x_n }$$$$G = \sqrt[4]{10 \cdot 15 \cdot 8 \cdot 40}$$$$G = \sqrt[4]{4800}$$$$G = 14,8\text{%} $$

In [17]:
#def media_geometrica(df, coluna):
#    multiplicacao = 1
#    contador = 0
#    valores = list(df[coluna].values)
#    n = len(valores)
#    for valor in valores:
#        multiplicacao = multiplicacao * valor
#    return multiplicacao ** (1/n)
In [18]:
df_geometrica = df.iloc[0:10]
lista = list(df_geometrica['salario'].values)
multiplicacao = 1

# por algum motivo quando passamos no dataframe da erro, tanto na função como em for simples

for valor in [80000, 30000, 25500, 175000, 120000, 222200, 136000, 219000, 141000, 147100]:
    multiplicacao = multiplicacao * valor

print(f'Média geométrica: {multiplicacao ** (1/len(df_geometrica.salario.values))}')
Média geométrica: 105840.76202833292
In [19]:
# com scipy
stats.gmean(df_geometrica['salario'])
Out[19]:
105840.76202833273

Mediana¶

A mediana é o valor que ocupa a posição central da série de observações, quando estão ordenadas em ordem crescente.

$$\text{mediana(x)} = \begin{cases} x_{(\frac{n+1}{2})},\text{se n ímpar}\\ \frac{x_{\frac{n}{2}} + x_{\frac{n}{2}+1}}{2}, \text{se n par} \end{cases}$$

Essa medida de tendência central é bastante útil se tivermos na amostra outliers, pois a mediana é robusta para esses casos.

In [20]:
def mediana(valores):
    ordem = sorted(list(valores))
    if len(ordem) % 2 == 0:
        meio = int(np.floor(len(ordem) / 2) - 1)
        meio1 = meio + 1
        mediana = (ordem[meio] + ordem[meio1]) / 2
    else:
        meio = int(np.floor(len(ordem) / 2))
        mediana = ordem[meio]
    return mediana

mediana(df['salario'])
Out[20]:
138000
In [21]:
# com pandas
df['salario'].median()
Out[21]:
138000.0

Moda¶

A moda é o valor que ocorre com maior frequência (aparece mais vezes na amostra). Em alguns casos pode haver mais de uma moda. Se for duas observações, será bimodal, três, trimodal e assim por diante.

Exemplo: Temos os seguintes dados : [1, 2, 3, 3, 4, 3, 5, 6, 7]

Nossa moda é o 3, que aparece três vezes na amostra.

In [22]:
# com pandas
df['salario'].mode()
Out[22]:
0    100000
Name: salario, dtype: int64

Medidas de dispersão¶

Desvio Médio Absoluto¶

É a média dos desvios dos valores a contar da média, colocando os valores em módulo para evitar um possível resultado igual a zero.

$$\text{DMA(x)} = \frac{\sum_{i=1}^{n}|{x_i - \overline{x}}|}{n} $$

Exemplo: Um aluno fez três provas ao longo do ano, tirando respectivamente 8, 4 e 9. Qual é o desvio médio absoluto de suas notas?

primeiro precisamos achar a média

$\overline{x} = \frac{8 + 4 + 9}{3} = \frac{21}{3} = 7$

$\text{DMA(x)} = \frac{|8 - 7| + |4 - 7| + |9 - 7|}{3} = \frac{6}{3} = 2 $

In [23]:
# em python
def dma(valores):
    media = sum(valores) / len(valores)
    return sum(np.abs(valores - media))/ len(valores)
In [24]:
dma(df['salario'])
Out[24]:
116197.07271554231
In [25]:
# usando pandas
df['salario'].mad()
Out[25]:
116197.07271554231

Variância¶

A variância é determinada pela média dos quadrados dos desvios em relação a média aritmética. Por meio dessa medida, podemos avaliar o quanto os dados estão dispersos em relação à média aritmética. Logo, quanto maior for a variância, maior a dispersão dos dados.

fórmula:

$$var(X) = s²=\frac{\sum_{i = 1}^{n} (x_i - \overline{x})²}{n - 1}$$

Uma dificuldade de trabalhar com a variância é que ela nos devolve a unidade ao quadrado, sendo difícil de observar quanto os dados estão dispersos.

In [26]:
# na mão
def variancia(valores):
    media = sum(valores) / len(valores)
    return sum((valores - media)**2)/ (len(valores) - 1)

variancia(df['salario'])
Out[26]:
451149321334.5485
In [27]:
# no pandas
df['salario'].var()
Out[27]:
451149321334.5485

Desvio Padrão¶

De forma a melhorar a compreensão da dispersão dos dados, temos o desvio padrão, que é a raiz quadrada da variância. Dessa forma, o desvio padrão é capaz de apontar com maior precisão a dispersão dos valores em relação à média aritmética.

Fórmula:

$$dp(X) = \sqrt{var(X)} = \sqrt{\sigma²}$$$$dp(X)= \sigma = \sqrt{\frac{\sum_{i = 1}^{n} (x_i - \overline{x})²}{n - 1}}$$
In [28]:
# na mão
def desvio_padrao(valores):
    media = sum(valores) / len(valores)
    return np.sqrt(sum((valores - media)**2)/ (len(valores) - 1))

desvio_padrao(df['salario'])
Out[28]:
671676.5005079071
In [29]:
# no pandas
df['salario'].std()
Out[29]:
671676.5005079071

Quantis¶

Segundo Bussab e Morettin (2010) tanto a média como o desvio padrão podem não ser medidas adequadas para representar um conjunto de dados, pois:

  1. são afetados, de forma exagerada, por valores extremos;
  2. apenas com estes dois valores não temos ideia da simetria ou assimetria da distribuição dos dados.

Uma medida que pode ser considerada no lugar é o quantil.

Podemo definir uma medida, chamada quantil de ordem p ou p-quantil, indicada por q(p) onde p é uma proporção qualquer, $0 < p < 1$, tal que 100p% das observações sejam menores do que q(p). Os quantis mais famosos são os que veremos no box plots, que são:

$$\text{q(0,25)} = q_1 \text{: 1º Quartil = 25º Percentil} $$$$\text{q(0,50)} = q_2 \text{: Mediana = 2º Quartil = 50º Percentil} $$$$\text{q(0,75)} = q_3 \text{: 3º Quartil = 75º Percentil} $$

Mostraremos como se calcula com um exemplo:

Exemplo: Temos o seguinte conjunto de valores: $10, 25, 23, 34, 8, 2, 7$

  • 1º Passo: colocar os valores em ordem crescente: $2, 7, 8, 10, 23, 25, 34$

  • 2º Passo: Achar a mediana (2º Quartil) dos valores. Como os valores são impares, a mediana será dada por $x_{\frac{n+1}{2}}$. Como $n$ é 7, $n+1$ será 8, $\frac{8}{2}=4$. Queremos o valor que está na posição 4, que em nosso caso é o 10.

  • 3º Achando a mediana, agora temos dois conjuntos de dados, um abaixo da mediana e um acima. O conjunto abaixo é: $2, 7, 8$. E o acima é: $23, 25, 34$. Agora devemos achar a mediana de cada conjunto para acharmos o 1º e o 3º quartil respectivamente. Com isso temos que o 1º Quartil é 7 e o 3º Quartil é 25.

Quando o conjunto de valores tiver um n de tamanho par, usamos o cálculo da mediana quando n for par, que é dado por $\frac{x_{\frac{n}{2}} + x_{\frac{n}{2}+1}}{2}$

Exemplo: Temos o seguinte conjunto de valores: $10, 25, 23, 34, 8, 2, 7, 40$

  • 1º Passo: colocar os valores em ordem crescente: $2, 7, 8, 10, 23, 25, 34, 40$

  • 2º Passo: Achar a mediana (2º Quartil) dos valores. Como os valores são pares, a mediana será dada por $x_{\frac{n}{2}} + x_{\frac{n}{2}+1}$. Como $n$ é 8, os valores serão 4 e 5. Queremos os valores que estão na posição 4 e 5, que em nosso caso são os valores 10 e 23. Calculando a mediana deles temos: $\frac{10 + 23}{2} = 16,5$

  • 4º Achando a mediana, agora temos dois conjuntos de dados, um abaixo da mediana e um acima. O conjunto abaixo é: $2, 7, 8, 10$. E o acima é: $23, 25, 34, 40$. Agora devemos achar a mediana de cada conjunto para acharmos o 1º e o 3º quartil respectivamente. Com isso temos que o 1º Quartil é 7,5 e o 3º Quartil é 29,5.

Um segundo método de achar os quartis é pela formula: $$Q_j = x_k + (\frac{j(n+1)}{4}-k)(x_{k+1}-x_k)$$ Onde:

  • $j = 1, 2$ e $3$
  • $k$ é o maior inteiro menor ou igual a $\frac{j(n+1)}{4}$
  • $n$ é o número de elementos do conjunto

Pegaremos o primeiro exemplo para usar a fórmula:

Valores: $10,25,23,34,8,2,7$

Valores ordenados: $2, 7, 8, 10, 23, 25, 34$

Vamos calcular o primeiro quartil:

sabemos que $\frac{j(n+1)}{4} = \frac{1(7+1)}{4} = \frac{8}{4} = 2$

Logo $k$ será igual a $2$. Substituindo na equação temos

$Q_1 = x_2 + (2-2)(x_{2+1}-x_2)$

$Q_1 = x_2 + (0)(x_{3}-x_2)$

$x_2$ = 7, vamos substituir

$Q_1 = 7 + (0)(8-7)$

$Q_1 = 7 + (0)(1)$

$Q_1 = 7$

OBS.: Por mais que o segundo método tenha uma fórmula, achar pelas medianas é uma forma mais simples.

Com Python, podemos utilizar a biblioteca pandas para achar os valores de forma fácil usando o método describe()

In [30]:
df['salario'].describe()
Out[30]:
count    3.755000e+03
mean     1.906956e+05
std      6.716765e+05
min      6.000000e+03
25%      1.000000e+05
50%      1.380000e+05
75%      1.800000e+05
max      3.040000e+07
Name: salario, dtype: float64

Podemos também usar o método quantile()

In [31]:
# se não passar nada pro método, por padrão é o quantil 50º ou mediana
df['salario'].quantile()
Out[31]:
138000.0
In [32]:
# 20º quantil
df['salario'].quantile(q=0.2)
Out[32]:
85000.0

Box Plot¶

O box plot nos devolve, de forma gráfica, as principais medidas vistas até agora: Mediana, primeiro e terceiro quartil e os valores mínimo e máximo. Os valores de mínimo e máximo são importantes para entendermos o intervalo (range) dos nossos dados.

Segundo Bussab e Morettin (2010) o box plot dá uma ideia da posição, dispersão, assimetria, caudas e dados discrepantes.

Vamos ver um caso com dados discrepantes

In [33]:
conjunto = [10,25,23,34,8,2,7,60]
df_conjunto = pd.DataFrame({'valores': conjunto})
In [34]:
df_conjunto.boxplot(figsize=(5, 3))
plt.show()

As fórmulas abaixo são para calcular os limites inferior e superior, que serão o intervalo onde os valores adjacentes (valores que não são outliers) estarão:

$LI = q_1 - 1,5 \cdot IIQ$

$LS = q_3 + 1,5 \cdot IIQ$

$IIQ$ é intervalo interquartil, que é dado por

$IIQ = q_3 - q_1$

Assimetria¶

A simetria nos diz aonde nosso conjunto de dados está mais concentrado.

  • Distribuição Simétrica: Quando a distribuição for simétrica, nos temos um único valor para moda, média e mediana ($\overline{x} = M_d = M_o$)

  • Distribuições Assimétricas: Quando a distribuição for assimétrica, nos temos valores diferentes para moda, média e mediana ($\overline{x} = M_d = M_o$)

    • Assimetria à esquerda (ou negativa): Quando os valores mais baixos das observações são predominantes, tendo uma cauda mais longa para esquerda,nesse caso a media é menor que a mediana que é menor que a moda ($\overline{x} < M_d < M_o$)
    • Assimetria à direita (ou positiva): Quando os valores mais altos das observações são predominantes,tendo uma cauda mais longa para direita, nesse caso a media é maior que a mediana que é maior que a moda ($\overline{x} > M_d > M_o$)

Sua fórmula é dada por: $$\text{assimetria} = M_3 = \frac{\sum_{i=1}^{n}(x_i - \overline{x})³}{n}$$

Uma desvantagem de utilizar a fórmula acima está na dependência da unidade de medida dos dados. Se tivermos trabalhando com dados em reais, a assimetria retornará reais ao cubo.

Para resolver isso, basta dividirmos pelo desvio padrão elevado ao cubo, trazendo o que chamamos de coeficiente momento de assimetria ($\alpha_3$)

$$\text{coeficiente momento de assimetria} = \alpha_3 = \frac{M_3}{s³}$$
  • Se coeficiente momento de assimetria = 0, então a curva é simétrica (curva normal)
  • Se coeficiente momento de assimetria > 0, então assimetria a direita (assimetria positiva)
  • Se coeficiente momento de assimetria < 0, então assimetria a esquerda (assimetria negativa)
In [35]:
def coeficiente_assimetria(valores):
    media = sum(valores) / len(valores)
    assimetria = sum((valores - media)**3) / len(valores)
    desvio_padrao = np.sqrt(sum((valores - media)**2) / (len(valores) - 1))
    return assimetria / (desvio_padrao ** 3)
In [36]:
coeficiente_assimetria(df['salario'])
Out[36]:
28.914816754066507
In [37]:
df['salario'].skew()
Out[37]:
28.937932169111605
In [38]:
conjunto_simetrico = [1, 1, 3, 3, 5, 5, 5, 7, 7, 9, 9]
df_simetrico = pd.DataFrame({'valores': conjunto_simetrico})
df_simetrico.plot(kind='kde', figsize=(5, 3))
plt.show()
print(f'O coeficente de momento de assimetria é: {df_simetrico["valores"].skew()}')
O coeficente de momento de assimetria é: 0.0
In [39]:
conjunto_assimetrico_direita = [1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 5, 7, 7, 9]
df_assimetrico_direita = pd.DataFrame({'valores': conjunto_assimetrico_direita})
df_assimetrico_direita.plot(kind='kde', figsize=(5, 3))
plt.show()
print(f'O coeficente de momento de assimetria é: {df_assimetrico_direita["valores"].skew()}')
O coeficente de momento de assimetria é: 0.7436128024718242
In [40]:
conjunto_assimetrico_esquerda = [1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 9, 9]
df_assimetrico_esquerda = pd.DataFrame({'valores': conjunto_assimetrico_esquerda})
df_assimetrico_esquerda.plot(kind='kde', figsize=(5, 3))
plt.show()
print(f'O coeficente de momento de assimetria é: {df_assimetrico_esquerda["valores"].skew()}')
O coeficente de momento de assimetria é: -0.6554279508966395
In [41]:
df.head(2)
Out[41]:
ano_trabalho nivel_experiencia tipo_de_contrato cargo salario moeda_salario salario_em_dolares pais_residencia tipo_trabalho local_companhia tamanho_companhia
0 2023 Senior/Especialista Tempo integral Principal Data Scientist 80000 EUR 85847 ES Home office ES Grande
1 2023 Pleno Contrato ML Engineer 30000 USD 30000 US Home office US Pequeno
In [42]:
# vendo nos dados que estamos analisando desde o começo
# aqui utilizamos o gráfico de histograma, mas com ele também é possível ver assimetria
# sendo mais um tipo de gráfico que podemos usar com esse propósito
df['salario_em_dolares'].plot(kind='hist', figsize=(5,3))
plt.show()

Vemos com o gráfico acima, que o salário em dólares das pessoas que trabalham com dados é uma distribuição assimétrica à direita.

Curtose¶

Curtose é o grau de achatamento de uma distribuição em relação a uma distribuição padrão. Essa medida mede o agrupamento de valores da distribuição em torno do centro. Quanto mais agrupados em torno do centro, maior será o valor da curtose.

Existem 3 classificações para a curtose:

  • Mesocúrtica: É a curva normal padrão
  • Leptocúrtica: É quando a medida de curtose é maior do que a da distribuição normal. Nesse caso existem mais valores no centro
  • Platicúrtica: É quando a medida de curtose é menor do que a da distribuição normal. Nesse caso os valores estão mais espalhados ao longo da distribuição.

Sua fórmula é dada por: $$\text{curtose} = M_4 = \frac{\sum_{i=1}^{n}(x_i - \overline{x})⁴}{n}$$

Dá mesma forma que dividimos no coeficiente de assimetria, dividiremos aqui para ajustar a unidade de medida.

Para resolver isso, basta dividirmos pelo desvio padrão elevado à quarta, trazendo o que chamamos de coeficiente momento de curtose ($\alpha_4$)

$$\text{coeficiente momento de curtose} = \alpha_4 = \frac{M_4}{s⁴}$$
In [43]:
mesocurtica = [1, 3, 3, 5, 5, 5, 5, 7, 7, 9]
leptocurtica = [1, 3, 5, 5, 5, 5, 5, 5, 7, 9]
platicurtica = [1, 1, 3, 3, 5, 5, 7, 7, 9, 9]
df_curtose = pd.DataFrame({'mesocurtica': mesocurtica, 'leptocurtica': leptocurtica,
                           'platicurtica': platicurtica})
df_curtose.plot(kind='kde', figsize=(5, 3))
plt.show()
print(f"A curtose da mesocurtica é: {df_curtose['mesocurtica'].kurtosis()}")
print(f"A curtose da leptocurtica é: {df_curtose['leptocurtica'].kurtosis()}")
print(f"A curtose da platicurtica é: {df_curtose['platicurtica'].kurtosis()}")
A curtose da mesocurtica é: 0.08035714285714235
A curtose da leptocurtica é: 1.6714285714285717
A curtose da platicurtica é: -1.3339285714285714
In [44]:
def coeficiente_curtose(valores):
    media = sum(valores) / len(valores)
    assimetria = sum((valores - media)**4) / len(valores)
    desvio_padrao = np.sqrt(sum((valores - media)**2) / (len(valores) - 1))
    return assimetria / (desvio_padrao ** 4)
In [45]:
coeficiente_curtose(df['salario'])
Out[45]:
1148.426386596344
In [46]:
# no pandas
df['salario'].kurtosis()
Out[46]:
1147.5673898192115

Transformações¶

Muitos procedimentos estatísticos são baseados nos dados estarem em um distribuição normal. Porém, em muitas situações, os dados possuem distribuições assimétricas, contém outliers, etc.

Uma forma de tentar transformar esses dados em um conjunto de dados com distribuição normal é a através de transformações.

Segundo Bussab e Morettin (2010), transformações frequentemente utilizadas são:

$$ x^{(p)} = \begin{cases} x^p,\text{se p > 0} \\ ln(x),\text{se p = 0} \\ -x^p,\text{se p < 0} \\ \end{cases}$$

onde p são valores experimentados em uma sequência, por exemplo: ...,-3, -2, -1, 0, 1, 2, 3,...

Para valores assimétricos à direita, utilize transformações com $0 < p < 1$, pois valores grandes de x decrescem com valores de p pequenos. Para distribuições assimétricas à esquerda, utilize $p > 1$

Vamos ver no Python.

In [47]:
df['salario'].plot(kind='kde', figsize=(5,3))
plt.show()
print(df['salario'].skew())
print(df['salario'].iloc[0:5])
28.937932169111605
0     80000
1     30000
2     25500
3    175000
4    120000
Name: salario, dtype: int64

Temos acima uma distribuição com assimetria à direita. Vamos tentar transforma-lá em uma distribuição normal

In [48]:
salario_transformado = np.log1p(df['salario'])
salario_transformado.plot(kind='kde', figsize=(5,3))
plt.show()
print(salario_transformado.skew())
print(salario_transformado.iloc[0:5])
0.7874033806913452
0    11.289794
1    10.308986
2    10.146473
3    12.072547
4    11.695255
Name: salario, dtype: float64

Agora temos uma distribuição mais próxima da normal. Porém como eu saberei o valor real depois de alguma análise? Podemos voltar a mesma distribuição usando a função que seja inversa a que usamos, no caso usamos uma função logarítmica, temos que usar uma função exponencial para reverter.

In [49]:
salario_volta = np.expm1(salario_transformado)
salario_volta.plot(kind='kde', figsize=(5,3))
plt.show()
print(salario_volta.skew())
print(salario_volta.iloc[0:5])
28.937932169111598
0     80000.0
1     30000.0
2     25500.0
3    175000.0
4    120000.0
Name: salario, dtype: float64

Vemos que os valores voltaram ao normal.

Referências¶

  1. Morettin, P. A., Bussab, W. O. Estatística Básica, 6. ed. – São Paulo : Saraiva, 2010.
  2. Morettin, P. A., Singer, J. M. Estatística e Ciência de Dados - São Paulo: USP, 2021.
  3. Bruce, P., Bruce, A., Gedeck, P. Pratical Statistics for Data Scientists, 2 ed. O'Reilly, 2020.

In [ ]:
 
Voltar ao site Voltar à página de estatística Ir para próximo artigo $\rightarrow$