Estatística - medidas e frequencias

O presente notebook, objetiva a utilização das linguagens R e Python (concomitantemente) de forma a realizar análises estatisticas e comparar a utilizaçao de ambas as linguagens.

dados utilizados: PNAD 2015.

Sobre os variáveis:

Informações adicionais:

  1. Foram elminados registros de Renda inválida
  2. Foram considerados somente os registros das pessoas de referencia de cada domicilio

Carrega pacotes

## Python
import pandas as pd
import numpy as np
import seaborn as sns
import scipy
import matplotlib.pyplot as plt
## R
library(tidyverse)
library(reticulate)

Como serão utilizadas ambas as linguagens, se faz necessário o carregamento de pacotes em chunks diferentes.

Carregando dados

## Python
pnad=pd.read_csv("E:\\sandro\\cursos\\formacao_data_science\\estats_medidas_e_frequencias\\datasets\\dados.csv")
## R
pnad=py$pnad

Como replicaremos o processo de análise e trasformação em ambas as linguagens, carregamos o pacote reticulate e em seguida atribuimos ao R o nome de mesmo objeto utilizado em python.

Visualiza dados

## Python
pnad.head(5)
##    UF  Sexo  Idade  Cor  Anos de Estudo  Renda    Altura
## 0  11     0     23    8              12    800  1.603808
## 1  11     1     23    2              12   1150  1.739790
## 2  11     1     35    8              15    880  1.760444
## 3  11     0     46    2               6   3500  1.783158
## 4  11     1     47    8               9    150  1.690631
## R
pnad |> head(5)
##   UF Sexo Idade Cor Anos de Estudo Renda   Altura
## 1 11    0    23   8             12   800 1.603808
## 2 11    1    23   2             12  1150 1.739790
## 3 11    1    35   8             15   880 1.760444
## 4 11    0    46   2              6  3500 1.783158
## 5 11    1    47   8              9   150 1.690631

De cara ja é possível identificar alguns problemas. Há clara despadronização nas variáveis, uma delas está em maiúsculo e as outras não. além de que, há espaço entre palavras na váriavel ‘Anos de Estudo’ é uma boa prática utilizarmos underline para evitar conflito futuro entre as linguagens.

Padronizando variáveis

## Python

# Transforma variáveis em maiusculo
pnad.columns=pnad.columns.str.upper()

# Renomeia variaveis
pnad.rename(columns={'ANOS DE ESTUDO':'ANOS_ESTUDO'}, inplace=True)

# visualiza primeiras linhas
pnad.head(5)
##    UF  SEXO  IDADE  COR  ANOS_ESTUDO  RENDA    ALTURA
## 0  11     0     23    8           12    800  1.603808
## 1  11     1     23    2           12   1150  1.739790
## 2  11     1     35    8           15    880  1.760444
## 3  11     0     46    2            6   3500  1.783158
## 4  11     1     47    8            9    150  1.690631
## R

# Transforma variáveis em maiúsculo
names(pnad) <- toupper(names(pnad))

# Renomeia variáveis
colnames(pnad)[5] <- 'ANOS_ESTUDO'

# visualiza primeiras linhas
head(pnad,5)
##   UF SEXO IDADE COR ANOS_ESTUDO RENDA   ALTURA
## 1 11    0    23   8          12   800 1.603808
## 2 11    1    23   2          12  1150 1.739790
## 3 11    1    35   8          15   880 1.760444
## 4 11    0    46   2           6  3500 1.783158
## 5 11    1    47   8           9   150 1.690631

Verifica valores nulos

## Python
pnad.isnull().sum().tolist
## <bound method IndexOpsMixin.tolist of UF             0
## SEXO           0
## IDADE          0
## COR            0
## ANOS_ESTUDO    0
## RENDA          0
## ALTURA         0
## dtype: int64>
## R
as.data.frame(colSums(is.na(pnad)))
##             colSums(is.na(pnad))
## UF                             0
## SEXO                           0
## IDADE                          0
## COR                            0
## ANOS_ESTUDO                    0
## RENDA                          0
## ALTURA                         0

Não valores faltantes no dataset.

Informações de tipagem de dados

## Python
pnad.info()
## <class 'pandas.core.frame.DataFrame'>
## RangeIndex: 76840 entries, 0 to 76839
## Data columns (total 7 columns):
##  #   Column       Non-Null Count  Dtype  
## ---  ------       --------------  -----  
##  0   UF           76840 non-null  int64  
##  1   SEXO         76840 non-null  int64  
##  2   IDADE        76840 non-null  int64  
##  3   COR          76840 non-null  int64  
##  4   ANOS_ESTUDO  76840 non-null  int64  
##  5   RENDA        76840 non-null  int64  
##  6   ALTURA       76840 non-null  float64
## dtypes: float64(1), int64(6)
## memory usage: 4.1 MB
## R
pnad |> glimpse()
## Rows: 76,840
## Columns: 7
## $ UF          <dbl> 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11~
## $ SEXO        <dbl> 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1~
## $ IDADE       <dbl> 23, 23, 35, 46, 47, 34, 57, 60, 50, 26, 46, 49, 52, 38, 45~
## $ COR         <dbl> 8, 2, 8, 2, 8, 8, 8, 8, 4, 8, 8, 8, 2, 8, 2, 2, 8, 8, 2, 8~
## $ ANOS_ESTUDO <dbl> 12, 12, 15, 6, 9, 12, 12, 12, 14, 12, 12, 6, 6, 12, 12, 16~
## $ RENDA       <dbl> 800, 1150, 880, 3500, 150, 790, 3150, 1700, 1800, 1150, 30~
## $ ALTURA      <dbl> 1.603808, 1.739790, 1.760444, 1.783158, 1.690631, 1.637906~

Apesar de todas as variáveis estarem como no tipo numérica, algumas delas tratam-se de variáveis do tipo Qualitativas Nominais.

Abaixo um breve demonstrativo sobre tipos de variáveis:

## Python

# altera tipagem de variáveis
pnad['UF']=pnad['UF'].astype(int).astype(str)
pnad['SEXO']=pnad['SEXO'].astype(int).astype(str)
pnad['COR']=pnad['COR'].astype(int).astype(str)
pnad['ANOS_ESTUDO']=pnad['ANOS_ESTUDO'].astype(int).astype(str)
## R

# altera tipagem de variaveis
df_tip <- pnad |> 
  select(UF,SEXO,COR, ANOS_ESTUDO) |> 
  mutate_if(is.numeric,as.character)

pnad <- pnad |> 
  select(-UF,-SEXO,-COR,-ANOS_ESTUDO) |> 
  cbind(df_tip) |> 
  select(UF,SEXO,IDADE,COR,ANOS_ESTUDO,RENDA,ALTURA)

rm(df_tip)

Visualiza observações únicas

## Python
sorted(pnad['UF'].unique())
## ['11', '12', '13', '14', '15', '16', '17', '21', '22', '23', '24', '25', '26', '27', '28', '29', '31', '32', '33', '35', '41', '42', '43', '50', '51', '52', '53']
## R
pnad |> select(UF) |> unique() |> arrange(UF) |> as.list()
## $UF
##  [1] "11" "12" "13" "14" "15" "16" "17" "21" "22" "23" "24" "25" "26" "27" "28"
## [16] "29" "31" "32" "33" "35" "41" "42" "43" "50" "51" "52" "53"

Quantidade de observações por variável (COR)

## Python
pnad['COR'].value_counts()
## 8    35925
## 2    31815
## 4     8391
## 0      357
## 6      352
## Name: COR, dtype: int64
## R
pnad |> group_by(COR) |> dplyr::summarise(n=n()) |> arrange(desc(n))
## # A tibble: 5 x 2
##   COR       n
##   <chr> <int>
## 1 8     35925
## 2 2     31815
## 3 4      8391
## 4 0       357
## 5 6       352

Frequência por COR

## Python
pnad['COR'].value_counts(normalize=True)
## 8    0.467530
## 2    0.414042
## 4    0.109201
## 0    0.004646
## 6    0.004581
## Name: COR, dtype: float64
## R
pnad |> group_by(COR) |> dplyr::summarise(n=n()) |> mutate(percent=n/sum(n)) |> select(-n) |> arrange(desc(percent))
## # A tibble: 5 x 2
##   COR   percent
##   <chr>   <dbl>
## 1 8     0.468  
## 2 2     0.414  
## 3 4     0.109  
## 4 0     0.00465
## 5 6     0.00458

Frequência por sexo

## Python

# serie de percentuais por sexo
percent=pnad['SEXO'].value_counts(normalize=True)

# serie de frequencia por sexo
freq=pnad['SEXO'].value_counts()

# dataframe com agregacao de frequencia e percentual
dist=pd.DataFrame({'FREQ':freq,'PERCENT':percent})

# substitui valores do index
dist.rename(index={0:'M',1:'F'},inplace=True)

# transforma o index em coluna
dist=dist.rename_axis('SEXO').reset_index()
dist
##   SEXO   FREQ   PERCENT
## 0    0  53250  0.692998
## 1    1  23590  0.307002
## R
pnad |>
  group_by(SEXO) |>
  dplyr::summarise(FREQ = n()) |>
  mutate(PER = FREQ / sum(FREQ),
         SEXO = plyr::mapvalues(SEXO,
                                from =c(0, 1),
                                to =c('M', 'F')))
## # A tibble: 2 x 3
##   SEXO   FREQ   PER
##   <chr> <int> <dbl>
## 1 M     53250 0.693
## 2 F     23590 0.307

Distribuição por cor e sexo


## Python

sexo={'0':'M',1:'F'}
cor={'0': 'Indígena',
'2': 'Branca',
'4': 'Preta',
'6': 'Amarela',
'8': 'Parda',
'9': 'Sem declaração'}
        
        
percent=pd.crosstab(pnad['SEXO'],pnad['COR'],normalize=True)
percent.rename(index=sexo,inplace=True)
percent.rename(columns=cor,inplace=True)
percent
## COR   Indígena    Branca     Preta   Amarela     Parda
## SEXO                                                  
## M     0.003332  0.288834  0.071603  0.003058  0.326171
## 1     0.001314  0.125208  0.037598  0.001523  0.141359
## R
pnad |> 
  group_by(SEXO,COR) |> 
  dplyr::summarise(n=n()) |> 
  as.data.frame() |> 
  mutate(n2=sum(n),
         FREQ=n/n2) |> 
  select(-n2,-n) |> 
  mutate(COR = plyr::mapvalues(COR,
                             from = c(0,2,4,6,8,9),
                             to = c('indigena','branca','preta','amarela','parda','sem decl.')),
         SEXO = plyr::mapvalues(SEXO,
                                from=c(0,1),
                                to=c('M','F'))) |> 
  mutate_if(is.character,toupper) |> 
  pivot_wider(names_from=COR,values_from=FREQ)
## # A tibble: 2 x 6
##   SEXO  INDIGENA BRANCA  PRETA AMARELA PARDA
##   <chr>    <dbl>  <dbl>  <dbl>   <dbl> <dbl>
## 1 M      0.00333  0.289 0.0716 0.00306 0.326
## 2 F      0.00131  0.125 0.0376 0.00152 0.141

Calculando a renda média por etnia e sexo

## Python
renda_media=pd.crosstab(pnad['SEXO'],
pnad['COR'],aggfunc='mean',values=pnad['RENDA'])

renda_media.rename(index=sexo,inplace=True)
renda_media.rename(columns=cor,inplace=True);renda_media
## COR      Indígena       Branca        Preta      Amarela        Parda
## SEXO                                                                 
## M     1081.710938  2925.744435  1603.861687  4758.251064  1659.577425
## 1     2464.386139  2109.866750  1134.596400  3027.341880  1176.758516
## R
pnad |> group_by(SEXO,COR) |> 
  summarise(RENDA_MEDIA = mean(RENDA)) |> 
 mutate(COR = plyr::mapvalues(COR,
                             from = c(0,2,4,6,8,9),
                             to = c('indigena','branca','preta','amarela','parda','sem decl.')),
         SEXO = plyr::mapvalues(SEXO,
                                from=c(0,1),
                                to=c('M','F'))) |> 
  mutate_if(is.character,toupper) |> 
  pivot_wider(values_from=RENDA_MEDIA,names_from=COR)
## # A tibble: 2 x 6
## # Groups:   SEXO [2]
##   SEXO  INDIGENA BRANCA PRETA AMARELA PARDA
##   <chr>    <dbl>  <dbl> <dbl>   <dbl> <dbl>
## 1 M        1082.  2926. 1604.   4758. 1660.
## 2 F        2464.  2110. 1135.   3027. 1177.

Classificando o salário mínimo

  • A: acima de 15.760
  • B: de 7.880, até 15.760
  • C: de 3.152 até 7.880
  • D: de 1.576 até 3.152
  • E: de 0 até 1.576

## Python
# verficando valores máx e min de salário
print('o valor minimo de salario é:', pnad['RENDA'].min())
## o valor minimo de salario é: 0
print('o valor maximo de salario é:',pnad['RENDA'].max())
## o valor maximo de salario é: 200000
## R
options(scipen = 99999)

# verificando valores máx e min de salário
range(pnad |> select(RENDA))
## [1]      0 200000
# limites de classes
classes = [0, 1576, 3152, 7880, 15760, 200000]

# labels
labels = ['E', 'D', 'C', 'B', 'A']

## Python

# cria percentual
percentual=pd.value_counts(pd.cut(x=pnad['RENDA'],bins=classes,labels=labels,include_lowest=True),
normalize=True)

# cria frequencia
frequencia=pd.value_counts(pd.cut(x=pnad['RENDA'],bins=classes,labels=labels,include_lowest=True))

# agrega frequencia e percentual em um dataframe
dist=pd.DataFrame({'FREQ':frequencia,'PERCENT':percentual})


# transforma o index em coluna
dist.sort_index(ascending=False,inplace=True)
dist=dist.rename_axis('CLASS').reset_index()

dist
##   CLASS   FREQ   PERCENT
## 0     A    608  0.007913
## 1     B   2178  0.028345
## 2     C   7599  0.098894
## 3     D  16700  0.217335
## 4     E  49755  0.647514
## R
# cria classes
dist=pnad |> 
  mutate(CLASS=ifelse(RENDA <=1576,'e',
                          ifelse(RENDA >1576 & RENDA <= 3152,'d',
                                 ifelse(RENDA >3152 & RENDA <=7880,'c',
                                        ifelse(RENDA>7880 & RENDA <= 15760,'b',
                                               ifelse(RENDA >15760,'a','x')))))) |> 
  select(CLASS,RENDA) |> 
  group_by(CLASS) |> 
  dplyr::summarise(FREQ=n()) |> 
  mutate(PERCENT=FREQ/sum(FREQ)) |> 
  mutate_if(is.character,toupper)
dist |> head()
## # A tibble: 5 x 3
##   CLASS  FREQ PERCENT
##   <chr> <int>   <dbl>
## 1 A       608 0.00791
## 2 B      2178 0.0283 
## 3 C      7599 0.0989 
## 4 D     16700 0.217  
## 5 E     49755 0.648

Regra de Sturges

Também é possível realizar essa classificação utilizando o regra de Sturges.

A Regra de Sturges é uma função linear em escala logarítmica, onde o máximo para 100.000 pontos de dados são 18 classes.

k=1+(10/3 log10 N)

# define o valor de n a partir do tamanho dos dados
n=pnad.shape[0];n
## 76840
# aplica a funcao de sturges e define o valor de k
k=1+(10/3)*np.log10(n)
k=int(round(k));k
## 17
# cria frequencia a partir do valor de k
frequencia=pd.value_counts(pd.cut(x=pnad['RENDA'],bins=k,include_lowest=True),sort=False)

# cria o percentual a partir do valor de k
percentual=pd.value_counts(pd.cut(x=pnad['RENDA'],bins=k,include_lowest=True),normalize=True,sort=False)

# cria agregacao com dataframe entre percetual e frequencia
dist2=pd.DataFrame({'FREQ':frequencia,'PERCENT':percentual})

# transforma o index em coluna
dist2=dist2.rename_axis('CLASS').reset_index()

dist2
##                        CLASS   FREQ   PERCENT
## 0      (-200.001, 11764.706]  75594  0.983784
## 1     (11764.706, 23529.412]   1022  0.013300
## 2     (23529.412, 35294.118]    169  0.002199
## 3     (35294.118, 47058.824]     19  0.000247
## 4     (47058.824, 58823.529]     16  0.000208
## 5     (58823.529, 70588.235]      5  0.000065
## 6     (70588.235, 82352.941]      4  0.000052
## 7     (82352.941, 94117.647]      1  0.000013
## 8    (94117.647, 105882.353]      6  0.000078
## 9   (105882.353, 117647.059]      0  0.000000
## 10  (117647.059, 129411.765]      1  0.000013
## 11  (129411.765, 141176.471]      0  0.000000
## 12  (141176.471, 152941.176]      0  0.000000
## 13  (152941.176, 164705.882]      0  0.000000
## 14  (164705.882, 176470.588]      0  0.000000
## 15  (176470.588, 188235.294]      0  0.000000
## 16    (188235.294, 200000.0]      3  0.000039
plt.close()
ax = sns.distplot(pnad.ALTURA, kde = False,color='blue')
## <string>:1: UserWarning: 
## 
## `distplot` is a deprecated function and will be removed in seaborn v0.14.0.
## 
## Please adapt your code to use either `displot` (a figure-level function with
## similar flexibility) or `histplot` (an axes-level function for histograms).
## 
## For a guide to updating your code to use the new functions, please see
## https://gist.github.com/mwaskom/de44147ed2974457ad6372750bbe5751
ax.figure.set_size_inches(12, 6)
ax.set_title('Distribuição de Frequências - Altura', fontsize=18)
ax.set_xlabel('Metros', fontsize=12)
ax.set_ylabel('Freq',fontsize=12)
plt.show()

plt.close()
ax=sns.barplot(data=dist,x='CLASS',y='FREQ')
ax.set_title('Frequency by class')
plt.show()

MÉDIA

# criando dataframe
df = pd.DataFrame(data = {'Fulano': [8, 10, 4, 8, 6, 10, 8],
                          'Beltrano': [10, 2, 0.5, 1, 3, 9.5, 10],
                          'Sicrano': [7.5, 8, 7, 8, 8, 8.5, 7]},
                 index = ['Matematica',
                          'Portugues',
                          'Ingles',
                          'Geografia',
                          'Histeria',
                          'Fisica',
                          'Quimica'])
df=df.rename_axis('Disciplina').reset_index()
df
##    Disciplina  Fulano  Beltrano  Sicrano
## 0  Matematica       8      10.0      7.5
## 1   Portugues      10       2.0      8.0
## 2      Ingles       4       0.5      7.0
## 3   Geografia       8       1.0      8.0
## 4    Histeria       6       3.0      8.0
## 5      Fisica      10       9.5      8.5
## 6     Quimica       8      10.0      7.0
# calculando somente a media de fulano
df['Fulano'].mean()
## 7.714285714285714
# calculando a renda media por sexo

pnad.groupby(['SEXO'])['RENDA'].mean()
## SEXO
## 0    2192.441596
## 1    1566.847393
## Name: RENDA, dtype: float64
# exercicio - localizando media dos homens
dataset = pd.DataFrame({
    'Sexo': ['H', 'M', 'M', 'M', 'M', 'H', 'H', 'H', 'M', 'M'],
    'Idade': [53, 72, 54, 27, 30, 40, 58, 32, 44, 51]
})

dataset.groupby(['Sexo'])['Idade'].mean().loc['H']
## 45.75
# realizando o pivot longer no dataset
df=pd.melt(df,id_vars='Disciplina',value_vars=['Fulano','Beltrano','Sicrano'])

# trasforma as varieveis em maiusculo
df.columns=df.columns.str.upper()

# renomeia variaveis
df.rename(columns={'VARIABLE':'ALUNO','VALUE':'NOTA'},inplace=True)

df
##     DISCIPLINA     ALUNO  NOTA
## 0   Matematica    Fulano   8.0
## 1    Portugues    Fulano  10.0
## 2       Ingles    Fulano   4.0
## 3    Geografia    Fulano   8.0
## 4     Histeria    Fulano   6.0
## 5       Fisica    Fulano  10.0
## 6      Quimica    Fulano   8.0
## 7   Matematica  Beltrano  10.0
## 8    Portugues  Beltrano   2.0
## 9       Ingles  Beltrano   0.5
## 10   Geografia  Beltrano   1.0
## 11    Histeria  Beltrano   3.0
## 12      Fisica  Beltrano   9.5
## 13     Quimica  Beltrano  10.0
## 14  Matematica   Sicrano   7.5
## 15   Portugues   Sicrano   8.0
## 16      Ingles   Sicrano   7.0
## 17   Geografia   Sicrano   8.0
## 18    Histeria   Sicrano   8.0
## 19      Fisica   Sicrano   8.5
## 20     Quimica   Sicrano   7.0
# calculando a media por disciplina
df.groupby(['DISCIPLINA'])['NOTA'].mean()
## DISCIPLINA
## Fisica        9.333333
## Geografia     5.666667
## Histeria      5.666667
## Ingles        3.833333
## Matematica    8.500000
## Portugues     6.666667
## Quimica       8.333333
## Name: NOTA, dtype: float64
# calculando a nota media de Fulano
df.groupby(['ALUNO'])['NOTA'].mean().loc['Fulano']
## 7.714285714285714
# Calculando a nota media de Fulano por disciplina
df[df.ALUNO == 'Fulano'].groupby(['DISCIPLINA'])['NOTA'].mean()
## DISCIPLINA
## Fisica        10.0
## Geografia      8.0
## Histeria       6.0
## Ingles         4.0
## Matematica     8.0
## Portugues     10.0
## Quimica        8.0
## Name: NOTA, dtype: float64

MEDIANA

# seleciona notas do fulano
notas_fulano=df[df.ALUNO=='Fulano'][['DISCIPLINA','NOTA']]

# organiza os valores de nota por ordem crescente
notas_fulano.sort_values(by='NOTA',ascending=True,inplace=True)

# reseta o index
notas_fulano.reset_index(inplace=True)

# elimina o index antigo do dataset
notas_fulano[['DISCIPLINA','NOTA']]
##    DISCIPLINA  NOTA
## 0      Ingles   4.0
## 1    Histeria   6.0
## 2  Matematica   8.0
## 3   Geografia   8.0
## 4     Quimica   8.0
## 5   Portugues  10.0
## 6      Fisica  10.0
# calcula o valor mediano de notas de fulano
notas_fulano['NOTA'].median()
## 8.0
# calcula o valor de nota mediana da disciplina Ingles
df[df.DISCIPLINA=='Ingles'][['NOTA']].median()
## NOTA    4.0
## dtype: float64
# selecionando amostra aleatoria
notas_beltrano=df[df.ALUNO == 'Beltrano'][['DISCIPLINA','NOTA']].sample(4, random_state=123)

# calculando mediana a partir da amostra aleatoria de beltrano
notas_beltrano['NOTA'].median()
## 2.5
# calculando mediana de renda por sexo
pnad.groupby(['SEXO'])['RENDA'].median()
## SEXO
## 0    1350.0
## 1     900.0
## Name: RENDA, dtype: float64

MODA

# plotando o valor que mais se repete na variavel NOTA
df[['NOTA']].mode()
##    NOTA
## 0   8.0
# valor que mais se repete na disciplina geografia

df[df['DISCIPLINA'] == 'Geografia']['NOTA'].mode()
## 0    8.0
## Name: NOTA, dtype: float64
# moda de idade da base do pnad
pnad['IDADE'].mode()
## 0    40
## Name: IDADE, dtype: int64

Explicacao sobre MEDIA, MEDIANA E MODA

plt.close()
ax=sns.distplot(pnad.query('RENDA < 20000').RENDA)
## <string>:1: UserWarning: 
## 
## `distplot` is a deprecated function and will be removed in seaborn v0.14.0.
## 
## Please adapt your code to use either `displot` (a figure-level function with
## similar flexibility) or `histplot` (an axes-level function for histograms).
## 
## For a guide to updating your code to use the new functions, please see
## https://gist.github.com/mwaskom/de44147ed2974457ad6372750bbe5751
ax.figure.set_size_inches(12,6)
ax
plt.show()

O plot possui assimetria à direita.

# criando objetos de moda, media e mediana
moda=pnad['RENDA'].mode()[0]
media=pnad['RENDA'].mean()
mediana=pnad['RENDA'].median()
# printa os resultados na tela
print('Moda:   ',moda)
## Moda:    788
print('Media:  ',media)
## Media:   2000.3831988547631
print('Mediana:',mediana)
## Mediana: 1200.0
# confirmando se o plot é simetrico a direita
moda<mediana<media
## True

Como o resultado é igual a true, de fato há um comportamento de asimetria com cauda a direita

# verificando a distribuiçao de altura
plt.close()
ax=sns.displot(pnad.ALTURA)
ax.figure.set_size_inches(12,6)
ax

plt.show()

# calculando medida
moda=pnad.ALTURA.mode()
media=pnad.ALTURA.mean()
mediana=pnad.ALTURA.median()


# printa os resultados na tela
print('Moda:   ', moda)
## Moda:    0    1.568128
## 1    1.671225
## 2    1.681659
## 3    1.692977
## 4    1.708163
## 5    1.708370
## 6    1.753842
## 7    1.779073
## 8    1.796462
## Name: ALTURA, dtype: float64
print('Media:  ',media)
## Media:   1.6995124540575741
print('Mediana:',mediana)
## Mediana: 1.6993247325

A serie é multimodal, e mediana e media tem valor muito aproximado à moda. Ou seja é caracteristico de simetria.

# serie de anos de estudo
plt.close()
ax=sns.distplot(pnad.ANOS_ESTUDO)
## <string>:1: UserWarning: 
## 
## `distplot` is a deprecated function and will be removed in seaborn v0.14.0.
## 
## Please adapt your code to use either `displot` (a figure-level function with
## similar flexibility) or `histplot` (an axes-level function for histograms).
## 
## For a guide to updating your code to use the new functions, please see
## https://gist.github.com/mwaskom/de44147ed2974457ad6372750bbe5751
ax.figure.set_size_inches(12,6)
plt.show()

A distribuição possui caracteristica de bastante irregularidade com picos e vales bastante acentuados.

Se considerarmos somente os picos, encontraremos um comportamento bastante parecido com o padrao de assimetria a esquerda, pois os mairoes picos aparecem conforme os valores do eixo x sobem.

# moda, media e mediana
pnad['ANOS_ESTUDO']=pnad['ANOS_ESTUDO'].astype(int)
moda=pnad.ANOS_ESTUDO.mode()[0]
media=pnad.ANOS_ESTUDO.mean().round()
mediana=pnad.ANOS_ESTUDO.median()

# print
print('Moda:   ',moda)
## Moda:    12
print('Media:  ',media)
## Media:   9.0
print('Mediana:',mediana)
## Mediana: 11.0
pnad.info()
## <class 'pandas.core.frame.DataFrame'>
## RangeIndex: 76840 entries, 0 to 76839
## Data columns (total 7 columns):
##  #   Column       Non-Null Count  Dtype  
## ---  ------       --------------  -----  
##  0   UF           76840 non-null  object 
##  1   SEXO         76840 non-null  object 
##  2   IDADE        76840 non-null  int64  
##  3   COR          76840 non-null  object 
##  4   ANOS_ESTUDO  76840 non-null  int32  
##  5   RENDA        76840 non-null  int64  
##  6   ALTURA       76840 non-null  float64
## dtypes: float64(1), int32(1), int64(2), object(3)
## memory usage: 3.8+ MB

moda>media>mediana
## False

Ou seja, se a moda é MAIOR que a mediana e a mediana é maior que a media, entao trata-se de uma assimetria à esquerda.

Se a moda é MENOR que a mediana e a mediana é maior que a media, entao trata-se de uma assimetria à direita

Medida de Dispersão

  • Quartis: divide a variavel em quatro partes iguais quanto ao numero de elemetos de cada uma;

  • Decis: divide em dez partes iguais;

  • Percentis: divide em cem partes iguais.

As medidades separatrizes não sao influenciadas por valores extremos (outliers).

Esses calculos permitem analises importantes a partir de um ponto de referencia, como o salario minimo por exemplo. Tambem poderemos construir classificacoes com essas medidas, como as classes A,B,C,D,e E.

# calculando o quantile = 0.5 (mediana)
pnad['RENDA'].quantile()
## 1200.0
# calculando os quartis
pnad['RENDA'].quantile([0.25,0.5,0.75])
## 0.25     788.0
## 0.50    1200.0
## 0.75    2000.0
## Name: RENDA, dtype: float64
# calculando os decis
pnad['RENDA'].quantile([i/10 for i in range(1,10)])
## 0.1     350.0
## 0.2     788.0
## 0.3     800.0
## 0.4    1000.0
## 0.5    1200.0
## 0.6    1500.0
## 0.7    1900.0
## 0.8    2500.0
## 0.9    4000.0
## Name: RENDA, dtype: float64
# calculando os percentis
pnad['RENDA'].quantile([i/100 for i in range(1,99)])
## 0.01        0.0
## 0.02        0.0
## 0.03        0.0
## 0.04       50.0
## 0.05      100.0
##          ...   
## 0.94     5400.0
## 0.95     6000.0
## 0.96     7000.0
## 0.97     8000.0
## 0.98    10000.0
## Name: RENDA, Length: 98, dtype: float64
plt.close()
ax = sns.distplot(pnad.IDADE,
                  hist_kws = {'cumulative': True},
                  kde_kws = {'cumulative': True})
## <string>:1: UserWarning: 
## 
## `distplot` is a deprecated function and will be removed in seaborn v0.14.0.
## 
## Please adapt your code to use either `displot` (a figure-level function with
## similar flexibility) or `histplot` (an axes-level function for histograms).
## 
## For a guide to updating your code to use the new functions, please see
## https://gist.github.com/mwaskom/de44147ed2974457ad6372750bbe5751
ax.figure.set_size_inches(14, 6)
ax.set_title('Distribuição de Frequências Acumulada', fontsize=18)
ax.set_ylabel('Acumulado', fontsize=14)
ax.set_xlabel('Anos', fontsize=14)
ax
plt.show()

# decis de idade
pnad.IDADE.quantile([i / 10 for i in range(1, 10)])
## 0.1    28.0
## 0.2    33.0
## 0.3    36.0
## 0.4    40.0
## 0.5    43.0
## 0.6    47.0
## 0.7    51.0
## 0.8    55.0
## 0.9    61.0
## Name: IDADE, dtype: float64

Com o retorno podemos constatar que, 40% das pessoas entrevistadas possuem até quarenta anos de idade ou que 90% possuem menos de 61 anos

plt.close()
ax = sns.distplot(pnad.IDADE,
                  hist_kws = {'cumulative': True},
                  kde_kws = {'cumulative': True},
                  bins = 10)
## <string>:1: UserWarning: 
## 
## `distplot` is a deprecated function and will be removed in seaborn v0.14.0.
## 
## Please adapt your code to use either `displot` (a figure-level function with
## similar flexibility) or `histplot` (an axes-level function for histograms).
## 
## For a guide to updating your code to use the new functions, please see
## https://gist.github.com/mwaskom/de44147ed2974457ad6372750bbe5751
ax.figure.set_size_inches(14, 6)
ax.set_title('Distribuição de Frequências Acumulada', fontsize=18)
ax.set_ylabel('Acumulado', fontsize=14)
ax.set_xlabel('Anos', fontsize=14)
ax
plt.show()

Box-Plot

plt.close()
ax = sns.boxplot( x = 'ALTURA', y = 'SEXO', data = pnad, orient = 'h')
ax.figure.set_size_inches(12, 4)
ax.set_title('Altura', fontsize=18)
ax.set_xlabel('Metros', fontsize=14)
ax
plt.show()

Comparando as medidas entre as duas series, nao veremos quase nenhuma diferença entre as variaveis, pois altura foi construida simetricamente.

plt.close()
ax = sns.boxplot( x = 'RENDA', data = pnad, orient = 'h')
ax.figure.set_size_inches(12, 4)
ax.set_title('Renda', fontsize=18)
ax.set_xlabel('R$', fontsize=14)
ax
plt.show()

plt.close()
ax = sns.boxplot( x = 'RENDA', y='SEXO', data = pnad.query('RENDA < 10000'),
orient = 'h')
ax.figure.set_size_inches(12, 4)
ax.set_title('Renda', fontsize=18)
ax.set_xlabel('R$', fontsize=14)
ax
plt.show()

plt.close()
ax = sns.boxplot( x = 'ANOS_ESTUDO', y = 'SEXO', data = pnad, orient = 'h')
ax.figure.set_size_inches(12, 4)
ax.set_title('Anos de Estudo', fontsize=18)
ax.set_xlabel('Anos', fontsize=14)
ax
plt.show()

Desvio médio absoluto

DM é igual ao somatorio dos desvios formado pelo modulo de X indice i que o valor de cada nota do df menos a media geral X. A ultima parte entre barras significa que somente pegaremos os valores absolutos ou seja, positivos.

Em um caso onde a média é maior do que o valor, como o complemento de X sendo igual a 10 e x no indice i for igual a 2 por exemplo, o resultado final da conta sera -8. Como queremos apenas 8, retiraremos o sinal de subtracao e entenderemos o porque adiante.

Aplicaremos esta estatistica para fulano na celula.

# cria dataframe de notas do fulano
notas_fulano=df[df.ALUNO == 'Fulano'][['DISCIPLINA','NOTA']]

# cria nota_media_fulano
nota_media_fulano=notas_fulano['NOTA'].mean()

# cria variavel de desvio
notas_fulano['DESVIO']=notas_fulano['NOTA']-nota_media_fulano

notas_fulano
##    DISCIPLINA  NOTA    DESVIO
## 0  Matematica   8.0  0.285714
## 1   Portugues  10.0  2.285714
## 2      Ingles   4.0 -3.714286
## 3   Geografia   8.0  0.285714
## 4    Histeria   6.0 -1.714286
## 5      Fisica  10.0  2.285714
## 6     Quimica   8.0  0.285714
# desvio absoluto
notas_fulano['|DESVIO|']=notas_fulano['DESVIO'].abs()

notas_fulano
##    DISCIPLINA  NOTA    DESVIO  |DESVIO|
## 0  Matematica   8.0  0.285714  0.285714
## 1   Portugues  10.0  2.285714  2.285714
## 2      Ingles   4.0 -3.714286  3.714286
## 3   Geografia   8.0  0.285714  0.285714
## 4    Histeria   6.0 -1.714286  1.714286
## 5      Fisica  10.0  2.285714  2.285714
## 6     Quimica   8.0  0.285714  0.285714
plt.close()
ax = notas_fulano['NOTA'].plot(style = 'o')
ax.figure.set_size_inches(14, 6)
ax.hlines(y = nota_media_fulano, xmin = 0, xmax = notas_fulano.shape[0] - 1, colors = 'red')
for i in range(notas_fulano.shape[0]):
    ax.vlines(x = i, ymin = nota_media_fulano, ymax = notas_fulano['NOTA'][i], linestyle='dashed')
ax
plt.show()

desvio_medio_absoluto = notas_fulano['NOTA'].mad()
desvio_medio_absoluto
## 1.5510204081632648
df = pd.DataFrame(data = {'Fulano': [8, 10, 4, 8, 6, 10, 8],
                          'Sicrano': [7.5, 8, 7, 8, 8, 8.5, 7]}, 
                  index = ['Matemática', 
                           'Português', 
                           'Inglês', 
                           'Geografia', 
                           'História', 
                           'Física', 
                           'Química'])
df.rename_axis('Matérias', axis = 'columns', inplace = True)
df
## Matérias    Fulano  Sicrano
## Matemática       8      7.5
## Português       10      8.0
## Inglês           4      7.0
## Geografia        8      8.0
## História         6      8.0
## Física          10      8.5
## Química          8      7.0
# dispersao desvio medio absoluto

# nota media fulano e sicrano
nota_media_fulano=df['Fulano'].mean()
nota_media_sicrano=df['Sicrano'].mean()


# datasets 
notas_fulano=df[['Fulano']]
notas_sicrano=df[['Sicrano']]

# desvios
notas_fulano['Desvio']=notas_fulano['Fulano']-nota_media_fulano
notas_sicrano['Desvio']=notas_sicrano['Sicrano']-nota_media_sicrano
notas_fulano['|desvio|']=notas_fulano['Desvio'].abs()
notas_sicrano['|desvio|']=notas_sicrano['Desvio'].abs()

# desvio medio absoluto
desvio_fulano=notas_fulano['Desvio'].mad()
desvio_sicrano=notas_sicrano['Desvio'].mad()

# print
print('------ DESVIO MEDIO ABSOLUTO ------')
## ------ DESVIO MEDIO ABSOLUTO ------
print('Fulano: ',desvio_fulano)
## Fulano:  1.551020408163265
print('Sicrano:',desvio_sicrano)
## Sicrano: 0.4693877551020408

Variância

É a soma do quadrado dos desvios dividido pelo numero total.

O primeiro calculo é representado por sigma ao quadrado, sendo igual ao somatorio dos desvios em relacao a media populacional representada por mi e dividido por n

Ja no segundo, dividremos por n-1 conhecido como fator de correcao de bessel. Utilizando-o teremos uma estimativa mais precido do parametro populacional.

notas_fulano['(Desvio)^2']=notas_fulano['Desvio'].pow(2)

notas_fulano['(Desvio)^2'].sum() / (len(notas_fulano) - 1)
## 4.57142857142857
variancia = notas_fulano['Fulano'].var()

Desvio Padrão

É a medida de dispersao mais conhecida.

Este calculo é justamente a raiz quadradada da variancia vista no passo anterior, cujo resultado é a media dos desvios ao quadrado.

desvio_padrao=notas_fulano['Fulano'].std()
desvio_padrao
## 2.1380899352993947
df.std()
## Matérias
## Fulano     2.138090
## Sicrano    0.566947
## dtype: float64

Essa estatistica mostra que sicrano possui o desvio padrao menor, ou seja, possui valores mais constantes e proximos, enquanto fulano é mais instável, o que é possivel observa diretamente no boletim de notas.

dataset = pd.DataFrame({
    'Sexo': ['H', 'M', 'M', 'M', 'M', 'H', 'H', 'H', 'M', 'M'],
    'Idade': [53, 72, 54, 27, 30, 40, 58, 32, 44, 51]
})
m=dataset[dataset.Sexo == 'M']['Idade']
h=dataset[dataset.Sexo=='H']['Idade']
i=dataset['Idade']

print('----- STD -----')
## ----- STD -----
print('h:',h.std())
## h: 11.89887949906769
print('m:',m.std())
## m: 16.64531966249572
print('i:',i.std())
## i: 14.184890239656813