Comecemos com alguns comandos para tabelas de múltipla entrada. Lembremos que alguns comandos já foram apresentados no Capítulo 4. Um pacote interessante é o pacote tables, que constrói tabelas de dupla entrada de forma semelhante ao proc tabulate do SAS através do comando tabular(table,...). Neste comando, o argumento table é uma expressão que especifica as linhas e colunas da tabela desejada. A expressão divide as linhas das colunas através de ~, i.e., antes do sinal ~ especificaremos as linhas e depois dele, as colunas. os sinais + e * indicam se desejamos apenas adicionar as variáveis ou cruzá-las, respectivamente. Além disso, para as variáveis quantitativas de interesse, podemos atribuir também o cálculo de funções, como médias e desvios-padrão, por exemplo. Dessa forma, considerando a Tabela 2.1 do Capítulo 2, se quisermos construir uma tabela com estado civil versus grau de instrução e idade média(em anos), a sintaxe seria:
library(tables)
load("dados.RData")
attach(tab2_1)
tabular( estado_civil ~ grau_instrucao*idade_anos*(mean) )
estado_civil |
grau_instrucao ensino fundamental idade_anos mean |
ensino médio idade_anos mean |
superior idade_anos mean |
|---|---|---|---|
| casado | 35.80 | 34.42 | 37.00 |
| solteiro | 37.71 | 31.50 | 29.67 |
Ou seja, temos que, na Tabela 2.1, 5 as pessoas casadas com ensino fundamental tem, em média, 35.8 anos, as casadas com ensino fundamental, 34.42 e assim por diante. Se quisessemos que o grau de instrução ficasse nas linhas, cruzado com o estado civil e, além da média, quiséssemos também o desvio-padrão da idade, a sintaxe seria:
tabular( estado_civil*grau_instrucao ~ idade_anos*(mean+sd) )
estado_civil |
grau_instrucao |
idade_anos mean |
sd |
|---|---|---|---|
| casado | ensino fundamental | 35.80 | 6.181 |
| ensino médio | 34.42 | 5.282 | |
| superior | 37.00 | 9.539 | |
| solteiro | ensino fundamental | 37.71 | 7.910 |
| ensino médio | 31.50 | 7.918 | |
| superior | 29.67 | 4.163 |
Por fim, para imprimir o total por linhas e colunas, basta adicionar o termo +1 à variável a qual se deseja o total:
tabular( (estado_civil + 1)*(grau_instrucao+1) ~ idade_anos*(mean+sd) )
estado_civil |
grau_instrucao |
idade_anos mean |
sd |
|---|---|---|---|
| casado | ensino fundamental | 35.80 | 6.181 |
| ensino médio | 34.42 | 5.282 | |
| superior | 37.00 | 9.539 | |
| All | 35.15 | 5.896 | |
| solteiro | ensino fundamental | 37.71 | 7.910 |
| ensino médio | 31.50 | 7.918 | |
| superior | 29.67 | 4.163 | |
| All | 33.88 | 7.805 | |
| All | ensino fundamental | 36.92 | 6.999 |
| ensino médio | 33.44 | 6.205 | |
| superior | 33.33 | 7.711 | |
| All | 34.58 | 6.737 |
Uma observação importante sobre este pacote é o fato de que as variáveis não-quantitativas devem ser da classe factor. Assim, se a variável de interesse não for deste tipo, é necessário primeiramente transformá-la utlizando o comando factor(). Por exemplo, na tabela anterior, caso estado civil não fosse um fator, seria necessário executar o comando da seguinte forma:
tabular( (factor(estado_civil) + 1)*(grau_instrucao+1) ~ idade_anos*(mean+sd) )
Vamos construir uma função que utilizaremos para imprimir um gráfico de dispersão com as distribuições marginais. O argumento type definirá o tipo de gráfico que teremos nas marginais: “histogram”, “boxplot” ou “dispersion”.
scatter.marginal <- function(x, y, type="histogram",breaks=NA,col="darkred",pch=20,method="jitter",...){
# method="jitter" define o tipo de dispersão que teremos na marginal quando type for "dispersion".
# Outras opções são "stack", para empilhar os pontos e "overplot", para sobrepô-los.
if(type=="histogram"){
m=matrix(c(2,0,1,3), ncol=2, byrow=TRUE) # define as regiões a serem utilizadas no gráfico.
layout(mat=m, widths=c(0.82,0.18), heights=c(0.18,0.82))
if(is.na(breaks)){
xhist = hist(x, plot=FALSE,breaks=min(max(5,floor(length(x)/7)),10)) # marginal x
yhist = hist(y, plot=FALSE,breaks=min(max(5,floor(length(x)/7)),10)) # marginal y
}
else{
xhist = hist(x, plot=FALSE,breaks=breaks) # marginal x
yhist = hist(y, plot=FALSE,breaks=breaks) # marginal y
}
top = max(c(xhist$counts, yhist$counts)) # maximo entre os dois eixos
par(mar=c(5,5,1,1))
plot(x,y,pch=pch,col=col,...) # Grafico principal
par(mar=c(0,5,1,1))
barplot(xhist$counts, axes=FALSE, ylim=c(0, top), space=0, col="lightgray",border="white") # imprime o grafico da marginal x
par(mar=c(5,0,1,1))
barplot(yhist$counts, axes=FALSE, xlim=c(0, top), space=0, horiz=TRUE, col="lightgray",border="white") # imprime o grafico da marginal y
}
if(type=="boxplot"){
m=matrix(c(2,0,1,3), ncol=2, byrow=TRUE)
layout(mat=m, widths=c(0.8,0.2), heights=c(0.18,0.82))
par(mar=c(5,5,1,1))
plot(x,y,pch=pch,col=col,...) # Grafico principal
par(mar=c(0,5,1,1))
boxplot(x, axes=FALSE, col="lightgray",pch=20, horizontal=TRUE) # marginal x
par(mar=c(5,0,1,1))
boxplot(y, axes=FALSE, col="lightgray",pch=20) # marginal y
}
if(type=="dispersion"){
m=matrix(c(2,0,1,3), ncol=2, byrow=TRUE)
layout(mat=m, widths=c(0.85,0.15), heights=c(0.18,0.82))
par(mar=c(5,5,1,1))
plot(x,y,pch=pch,col=col,...) # gráfico principal
par(mar=c(0,5,1,1))
stripchart(x, method = method, pch = 20, col="gray",vertical = FALSE,yaxt="n",xaxt="n") # marginal x
par(mar=c(5,0,1,1))
stripchart(y, method = method, pch = 20, col="gray",vertical = TRUE,yaxt="n",xaxt="n") # marginal y
}
}
Para imprimir o gráfico utilizaremos a função que acabamos de construir:
load("dados.RData")
scatter.marginal(tab2_1$idade_anos, tab2_1$salario, ylab="salario", xlab="idade")
Figura 8.A: Gráfico de dispersão utilizando histogramas nas marginais
scatter.marginal(tab2_1$idade_anos, tab2_1$salario, ylab="salario", xlab="idade",type="boxplot")
Figura 8.B: Gráfico de dispersão utilizando boxplots nas marginais
scatter.marginal(tab2_1$idade_anos, tab2_1$salario, ylab="salario", xlab="idade",type="dispersion")
Figura 8.C: Gráfico de dispersão utilizando dispersão nas marginais
Pode-se também utilizar os pacotes ggplot2 e ggExtra para a construção destes gráficos. Os comandos para a construção das figuras a 8.A a 8.C são:
attach(tab2_1)
library(ggplot2)
library(ggExtra)
dispersao = ggplot(tab2_1, aes(idade_anos,salario)) + geom_point() #Dispersao
ggMarginal(dispersao, type="histogram") # Marginais com histograma
dispersao = ggplot2::ggplot(tab2_1, ggplot2::aes(idade_anos, salario)) + ggplot2::geom_point() #Dispersao
ggMarginal(dispersao,type = "boxplot") # Marginais com boxplot
dispersao = ggplot2::ggplot(tab2_1, ggplot2::aes(idade_anos, salario)) + ggplot2::geom_point() #Dispersao
scatter <- ggplot(tab2_1, aes(idade_anos,salario)) + geom_point() + #dispersão
scale_x_continuous(limits=c(min(idade_anos),max(idade_anos))) + # marginal X
scale_y_continuous(limits=c(min(salario),max(salario))) + # marginal Y
geom_rug(size=0.1) + theme_set(theme_minimal(base_size = 18))
scatter
É possivel também imprimir as marginais em formato de densidade:
ggMarginal(dispersao,type = "density") # Marginais com função densidade empírica
Outro gráfico útil para análise de v.a.’s multivariadas é o gráfico de dispersão em pares, ou gráficos de perfis. São úteis especialmente quando temos 3 ou mais variáveis e temos interesse em analisar o comportamento de uma em função das outras. Podemos construí-los com a função pairs(...). Ainda considerando a Tabela 2.1, Podemos cruzar as variáveis n_filhos, salario e idade_anos para identificar possíveis padrões de correlação entre elas:
pairs( ~ n_filhos + salario + idade_anos, pch=16, col="blue")
Note que os gráficos abaixo da diagonal principal são equivalentes os gráficos acima da diagonal principal, transpostos. Um opção útil para situações em que há muitas variáveis é imprimir apenas abaixo (ou acima) da diagonal principal, utilizando a opção upper.panel = NULL:
pairs( ~ n_filhos + salario + idade_anos, pch=16, col="blue",upper.panel = NULL)
OBS: para suprimir o os gráficos abaixo da diagonal, a opção é lower.panel=NULL.
Outros gráficos úteis são os gráficos de curvas de níveis, que são utilizados quando temos 3 variáveis contínuas, sendo duas explicativas(digamos, \(X\) e \(Y\)) e 1 resposta(\(Z\)). Neles, as vairações na escala de \(Z\) são apresentadas em função de cortes projetados no plano \(X \times Y\). São semelhantes aos “mapas de calor”, que apresentam as diferenças entre os níveis por variações de tons de cor. O pacote graphics possui a função filled.contour, que produz tais mapas de calor. Mapas de calor também são conhecidos como gráficos de contorno.
No exemplo a seguir, com dados extraídos de help(filled.contour), apresentamos a altitude do vulcão Maunga Whau, na Nova Zelândia, em função de sua latitude e longitude. No gráfico da Figura 8.A temos as curvas de nível e na Figura 8.B, o mapa de calor.
contour(x=1:nrow(volcano), y=1:ncol(volcano), volcano, col="darkred",xlab = "Sul - Norte", ylab = "Leste - Oeste")
title("Mapa topográfico: Maunga Whau", font = 4) # Titulo do Gráfico
Figura 8.A: Gráfico de curvas de nível do vulcão Maunga Whau em função da latitude e longitude.
library(colorRamps) # Paleta de cores que contem a sequencia de cores `blue2red`.
filled.contour(x=1:nrow(volcano), y=1:ncol(volcano), z=volcano, color = blue2red,
plot.title = title(main = "Mapa topográfico: Maunga Whau \n ",
xlab = "Sul - Norte", ylab = "Leste - Oeste"),
plot.axes = { axis(1, seq(100, 800, by = 100))
axis(2, seq(100, 600, by = 100)) },
key.axes = axis(4, seq(90, 190, by = 10)))# outra sugestão de cor: `color = terrain.colors` ou não declarar esta opção para visualizar a cor padrão.
Figura 8.B: Mapa de calor do vulcão Maunga Whau em função da latitude e longitude.
Para calcular a covariância amostral entre duas variáveis X e Y, utilize o comando cov(X,Y). Considerando os dados de salário e idade em anos temos que a covariância entre elas é:
cov(salario,idade_anos)
## [1] 11.231
cov(idade_anos,salario)
## [1] 11.231
Note que a ordem de entrada das variáveis não influencia no valor da covariância.
Para simular uma v.a. Normal multivariada, pode-se utilizar a função rmvnorm da bilioteca mvtnorm. Simulando então Um v.a. Normal bivariada X, com média \(\mu\) e variância \(\Sigma\) dadas por:
\[ \mu=\left(\begin{array}{c} 0 \\ 0 \end{array}\right)\\ \Sigma=\left(\begin{array}{cc} 1 & 0.5 \\ 0.5 & 1 \end{array}\right) \]
Teremos:
library(mvtnorm)
Sigma=cbind(c(1,0.6),c(0.6,1)) # matriz de variancias e covariancias
mu=c(0,0) # Vetor de médias
Z.sim<-rmvnorm(n = 1000, mean=mu, sigma=Sigma) # simula 100 observações de uma normal bivariada
Para calcular probabilidade conjunta, isto é, a F.D.A., utilizamos a função dmvnorm para construir uma função mais amigável para o usuário:
fdamvnorm<-function(upper,lower=c(-Inf,-Inf),mu=rep(0,times=length(upper)),Sigma=diag(length(upper))){
return(pmvnorm(lower=c(-Inf,-Inf),upper=upper,mean=mu,sigma=Sigma)[[1]]) # Calcula a integral dupla da fdp da normal multivariada entre no retangulo `lower X upper`.
}
fdamvnorm(upper=c(0,0)) # FDA considerando o terceiro quadrante
## [1] 0.25
fdamvnorm(upper=c(Inf,0)) # FDA considerando o terceiro e quarto quadrantes
## [1] 0.5
fdamvnorm(c(0,Inf)) # FDA considerando o segundo e terceiro quadrantes
## [1] 0.5
fdamvnorm(lower=c(-1.96,-1.96),upper=c(1.96,1.96)) #FDA considerando o quadrado (-1.96,-1.96)X(1.96,1.96)
## [1] 0.95063
A função densidade conjunta da distribuição normal multivariada é calculada com a função dmvnom, com a sintaxe muito semelhante à sintaxe da função rmvnorm. Se quisermos, por exemplo, o valor da curva da função no ponto (0,0), para \(\mu\) e \(\Sigma\) como definidos anteriormente, teremos que o valor é 0.19894:
Sigma=cbind(c(1,0.6),c(0.6,1)) # matriz de variancias e covariancias
mu=c(0,0) # Vetor de médias
dmvnorm(x=c(0,0), mean=mu, sigma=Sigma)
## [1] 0.19894
Precisamos calcular o valor da função densidade para uma região do plano \(XY\). Vamos então percorrer estes dois eixos entre os valores -4 e 4, amostrando a função densidade a cada 0.1:
xy<-seq(-4,4,0.1)
mu=c(0,0)
Sigma=diag(2)
Z1<-matrix(NA,length(xy),length(xy))
for(i in 1:length(xy)){
for(j in 1:length(xy)){
Z1[i,j]<-dmvnorm(x=c(xy[i],xy[j]), mean=mu, sigma=Sigma)
}
}
Para construir o gráfico de dispersão 3D de uma v.a. normal multivariada, utilizaremos a função persp do pacote graphics. Basta então utilizar o comando abaixo:
persp(Z1, phi = 30, theta = -45,col=heat.colors(ncol(Z1)*nrow(Z1),.8), ticktype = "detailed",xlab="X")
# `phi` e `theta` são os parâmetros de deslocamento da perspectiva da figura.
# heat.colors(.) modifica a paleta de cores do gráfico
# box=FALSE elimina a caixa que envolve o gráfico
# scale =FALSE define que as variáveis não devem ser padronizadas
# ticktype: tipo de eixos. Pode assumir valores "simple" ou "detailed"
Figura 8.15(a): f.d.p. de normais bidimensionais \(\mu_x=\mu_y=0,\sigma_x\sigma_y=1,\rho=0\);
Modificando agora o valor de \(\rho\) para \(\rho=0.6\) temos:
xy<-seq(-4,4,0.1)
mu=c(0,0)
Sigma=cbind(c(1,0.6),c(0.6,1))
Z2<-matrix(NA,length(xy),length(xy))
for(i in 1:length(xy)){
for(j in 1:length(xy)){
Z2[i,j]<-dmvnorm(x=c(xy[i],xy[j]), mean=mu, sigma=Sigma)
}
}
persp(Z2, phi = 30, theta = -45,col=heat.colors(ncol(Z2)*nrow(Z2),.8), ticktype = "detailed", xlab="X")
Figura 8.15(b): f.d.p. de normais bidimensionais \(\mu_x=\mu_y=0,\sigma_x\sigma_y=1,\rho=0.6\);
Como vimos anteriormente, as curvas de nível são construídas a partir do comando contour:
par(mfrow=c(1,2))
contour(x=1:nrow(Z2), y=1:ncol(Z2), Z2, col="darkred",xlab = "X", ylab = "Y")
contour(x=1:nrow(Z1), y=1:ncol(Z1), Z1, col="darkred",xlab = "X", ylab = "Y")
Figura 8.17: Curvas de nível para a normal bidimensional