A análise de cluster ou clustering é a tarefa de agrupar um conjunto de objetos de tal forma que os objetos no mesmo grupo (chamados de cluster) sejam mais parecidos (em algum sentido ou outro) uns com os outros do que com aqueles em outros grupos (clusters). É uma tarefa importante na fase exploratória de dados, e uma técnica comum para análise de dados estatísticos, usada em muitos campos, incluindo aprendizagem de máquina, reconhecimento de padrões, análise de imagem, recuperação de informação, bioinformática, compressão de dados e computação gráfica.
Análise de cluster em si não é um algoritmo específico, mas é a tarefa geral a ser resolvida. Pode ser alcançado por vários algoritmos que diferem significativamente em sua noção do que constitui um cluster e como encontrá-los eficientemente. As noções populares de clusters incluem grupos com pequenas distâncias entre os membros do cluster, áreas densas do espaço de dados, intervalos ou distribuições estatísticas particulares. Clustering pode, portanto, ser formulado como um problema de otimização multi-objetivo. O algoritmo de agrupamento apropriado e as configurações de parâmetros (incluindo valores como a função de distância a ser usada, um limite de densidade ou o número de clusters esperados) dependem do conjunto de dados individuais e do uso pretendido dos resultados. A análise de cluster como tal não é uma tarefa automática, mas um processo iterativo de descoberta de conhecimento ou otimização multi-objetivo interativa que envolve julgamento e falha. Muitas vezes é necessário modificar o pré-processamento de dados e os parâmetros do modelo até que o resultado obtenha as propriedades desejadas.
Uma cópia desse experimento, contendo programas fonte, dataset e demais arquivos pode ser descarregados em: Customer Cluster Analysis
Estou usando a Linguagem R neste experimento por questões de comodidade. O ideal é não ficar preso a uma ferramenta. Outras opções de clusterização, como o Cluto, podem ser vantajosas em situações com dados mais volumosos.
A versão da Linguagem R segue abaixo:
version
## _
## platform x86_64-w64-mingw32
## arch x86_64
## os mingw32
## system x86_64, mingw32
## status
## major 3
## minor 3.0
## year 2016
## month 05
## day 03
## svn rev 70573
## language R
## version.string R version 3.3.0 (2016-05-03)
## nickname Supposedly Educational
As informações encontradas são armazenadas em cache (pela biblioteca) para a sessão R eo argumento de campos especificados e atualizadas somente se o diretório da biblioteca de nível superior tiver sido alterado, por exemplo, instalando ou removendo um pacote. Se as informações em cache ficarem confusas, ela pode ser atualizada executando installed.packages (noCache = TRUE). O objetivo aqui é manter a reprodutibilidade do experimento.
is.installed <- function(mypkg) is.element(mypkg, installed.packages()[,1])
Esta função converte, em tempo de execução, a idade de cada cliente. Esta função não é exatamente necessária, mas demonstra uma transformação dos dados. Isso pode ocorrer em outros momentos e em outros dados dependendo da situação. Algo contruído para essa tarefa especificamente.
age_years <- function(earlier, later) {
lt <- data.frame(earlier, later)
age <- as.numeric(format(lt[,2],format="%Y")) - as.numeric(format(lt[,1],format="%Y"))
dayOnLaterYear <- ifelse(format(lt[,1],format="%m-%d")!="02-29",
as.Date(paste(format(lt[,2],format="%Y"),"-",format(lt[,1],format="%m-%d"),sep="")),
ifelse(as.numeric(format(later,format="%Y")) %% 400 == 0 | as.numeric(format(later,format="%Y")) %% 100 != 0 & as.numeric(format(later,format="%Y")) %% 4 == 0,
as.Date(paste(format(lt[,2],format="%Y"),"-",format(lt[,1],format="%m-%d"),sep="")),
as.Date(paste(format(lt[,2],format="%Y"),"-","02-28",sep=""))))
age[which(dayOnLaterYear > lt$later)] <- age[which(dayOnLaterYear > lt$later)] - 1
age
}
if(!is.installed("ggplot2"))
install.packages("ggplot2", dep = TRUE)
if(!is.installed("cluster"))
install.packages("cluster")
if(!is.installed("factoextra"))
install.packages("factoextra")
if(!is.installed("nnet"))
install.packages("nnet", dep = TRUE)
if(!is.installed("xlsx"))
install.packages("xlsx")
if(!is.installed("lazyeval"))
install.packages("lazyeval", dep = TRUE)
if(!is.installed("RCurl"))
install.packages("RCurl", dep = TRUE)
if(!is.installed("fpc"))
install.packages("fpc", dep = TRUE)
if(!is.installed("clValid"))
install.packages("clValid", dep = TRUE)
As mensagens e avisos foram retiradas ou suprimidas para efeito de apresentação. Os erros serão mostrados caso algum seja encontrado durante a carga da biblioteca.
library("ggplot2")
library("cluster")
library("factoextra")
library("nnet")
library("ClustOfVar")
library("lazyeval")
library("fpc")
require("xlsx")
require("RCurl")
Inicializa ambiente de pesquisa reprodutível
Não há problemas em alterar o valor abaixo, desde que isso não seja feito no mesmo experimento, o objetivo aqui, mais uma vez é a reprodutibilidade.
set.seed(1608)
O arquivo está no formato MS Excel, a planilha de interesse é a “Dados”. A variável maxLoad é usada para limitar a carga, pois originalmente o volume de linhas não são recomendadas para efeitos de teste. Eu usei a variável com tamanho de 51, mas para a clusterização de todo dataset o conteúdo deve ser NULL.
maxLoad <-NULL
mydata <- read.xlsx("datasets/Dataset-CodeChallengeDataScientist.xlsx",
sheetName = "Dados",endRow = maxLoad)
head(mydata,3)
## ID GEO_REFERENCIA DATA_NASCIMENTO PROFISSAO GENERO
## 1 1 780 1992-08-15 ANALISTA DE SISTEMAS M
## 2 2 35 1990-02-24 SERVIDOR PÃBLICO ESTADUAL F
## 3 3 54 1987-07-17 ANALISTA DE SISTEMAS M
## ESTADO_CIVIL VALOR_01 VALOR_02 VALOR_03 VALOR_04 PERFIL
## 1 SOLTEIRO(A) 342.8571 342.8571 428.5714 28.57143 A
## 2 SOLTEIRO(A) 942.8571 0.0000 0.0000 0.00000 A
## 3 SOLTEIRO(A) 2000.0000 0.0000 0.0000 2857.14286 A
mydata <- data.frame(mydata,stringsAsFactors = TRUE)
Remove dados não disponíveis (NA), caso existam, e executa a normalização das variáveis quantitativas.
mydata <- na.omit(mydata)
# scale é uma função genérica cujo método padrão centra e / ou escala as colunas de uma matriz numérica
mydata$DATA_NASCIMENTO <- age_years(as.Date(mydata$DATA_NASCIMENTO),as.Date(Sys.Date()))
mydata$GEO_REFERENCIA <- scale(mydata$GEO_REFERENCIA)
mydata$VALOR_01 <- scale(mydata$VALOR_01)
mydata$VALOR_02 <- scale(mydata$VALOR_02)
mydata$VALOR_03 <- scale(mydata$VALOR_03)
mydata$VALOR_04 <- scale(mydata$VALOR_04)
# Alterando os nomes das colunas para melhor visualização gráfica
names(mydata) <- c("ID","GEO","Age","Prof","Gen","EstCiv","V1","V2","V3","V4","Per")
# Após o processamento, logo abaixo são mostradas as mesmas três primeiras linhas normalizadas
head(mydata,3)
## ID GEO Age Prof Gen EstCiv
## 1 1 1.4738105 24 ANALISTA DE SISTEMAS M SOLTEIRO(A)
## 2 2 -1.0036432 26 SERVIDOR PÃBLICO ESTADUAL F SOLTEIRO(A)
## 3 3 -0.9404598 29 ANALISTA DE SISTEMAS M SOLTEIRO(A)
## V1 V2 V3 V4 Per
## 1 -0.275436918 -0.2051472 -0.1014105 -0.21563039 A
## 2 -0.177057282 -0.2089918 -0.1127948 -0.21685948 A
## 3 -0.003721733 -0.2089918 -0.1127948 -0.09395066 A
Existem várias abordagens para a clusterização, como por exemplo, Métodos de Particionamento, Métodos Hierárquicos, Métodos Baseados em Densidade, Métodos Baseados em Grid, Métodos Baseados em Modelos (Model-based). Neste documento, descreverei três: aglomeração hierárquica, particionamento e Model-based. Embora não haja melhores soluções para o problema de determinar o número de aglomerados a extrair, ou melhor, quantos clusters, optamos pelas abordagens abaixo.
As variáveis podem ser quantitativas, qualitativas ou uma distribuição de ambas. O critério de agregação é a diminuição da homogeneidade para o cluster que está sendo mesclado. A homogeneidade de um cluster é a soma da razão de correlação (para variáveis qualitativas) e da correlação quadrática (para variáveis quantitativas) entre as variáveis e o centro do cluster (centroide) que é o primeiro componente principal de PCA mix.
PCA mix é definido para uma distribuição de variáveis qualitativas e quantitativas e inclui análises de componentes principais comuns (PCA) e análise de correspondência múltipla (MCA) como casos especiais. Os valores em falta são substituídos por médias para variáveis quantitativas e por zeros na matriz de indicadores para a variável qualitativa.
Vamos separar o dataset em duas partes, qualitativa como “a” e quantitativa como “x” Vamos também ignorar o ID do cliente para efietos de clusterização, poiis queremos nesse momento entender o dataset e como ele se comporta.
a <- mydata[,c(4:6,11)]
x <- mydata[,c(2,3,7:10)]
mydata <- mydata[,2:11]
Observando a figura abaixo, podemos verificar que ela sugere um número possível de clusters. Usamos uma validação k-fold de 10.
tree <- hclustvar(x,a)
stab <- stability(tree, graph = FALSE,B = 10)
Número de Cluster sugeridos
nrCluster <- which.is.max(stab$meanCR)
nrCluster
## [1] 7
Número de Cluster sugeridos em formato gráfico
plot(stab)
Um dendrograma (do grego dendro “árvore” e gramma “desenho”) é um diagrama de árvore freqüentemente usado para ilustrar o arranjo dos clusters produzidos por agrupamento hierárquico. Dendrogramas são freqüentemente usados em biologia computacional para ilustrar o agrupamento de genes ou amostras, às vezes em cima de heatmaps. (Wikipédia).
Em nosso caso os clusters são mostrados dentro dos blocos vermelhos.
plot(tree)
rect.hclust(tree, k=nrCluster, border="red")
Detalhamento
tree
##
## Call:
## hclustvar(X.quanti = x, X.quali = a)
##
##
## number of variables: 10
## number of numerical variables: 6
## number of categorical variables: 4
## number of objects: 4972
##
## available components: height clusmat merge
Para ajudar a confirmar o número provável de clusters, usaremos o gráfico no formato Boxplot com o índice de Rand ajustado.
boxplot(stab$matCR, main="Dispersão do índice Rand ajustado")
O índice Rand ou Rand (nomeado por William M. Rand) em estatística, e em particular no agrupamento de dados, é uma medida da similaridade entre dois agrupamentos de dados. Uma forma do índice Rand pode ser definida e ajustada para o agrupamento casual de elementos, este é o índice Rand ajustado. Do ponto de vista matemático, o índice Rand está relacionado com a precisão, mas é aplicável mesmo quando os rótulos das classes não são usadas, ou seja, agrupamentos ou clusters.
K-means clustering é o método de particionamento mais popular. Exige que o analista especifique o número de clusters a serem extraídos. Um gráfico da soma de quadrados de grupos por número de aglomerados extraídos pode ajudar a determinar o número apropriado de clusters. O analista procura uma curva na trama semelhante a um teste de análise fatorial.
Para a conveniência da visualização, tomamos os dois primeiros componentes principais como as novas variáveis de característica e realizamos k-means somente nestes dados bidimensionais.
pca <- princomp(x, cor=T)
pc.comp <- pca$scores
pc.comp1 <- -1*pc.comp[,1]
pc.comp2 <- -1*pc.comp[,2]
newComp <- cbind(pc.comp1, pc.comp2)
cl <- kmeans(newComp, nrCluster)
plot(pc.comp1, pc.comp2,col=cl$cluster,xlab = "Componente 1", ylab = "Componente 2",
main = "KMeans & PCA")
points(cl$centers, pch=16)
O algorítmo kmeans executa a análise de agrupamento e fornece os resultados de agrupamento e seus centroides, para ser exato, o vetor centróide (ou seja, a média) para cada cluster.
Observa-se aqui que um dos clusters está linearmente separável dos demais. Os outros estão coesos. Observa-se também que quanto maior o valor da primeira componente, mais esparço ficam os elementos do cluster, isso pode justificar essa separabilidade.
Sugerimos o uso de regressão linear, ou quadrática dependendo dos resultados da linear, para identificar relações entre as variáveis qualitativas, como por exemplo a idade ou perfil com as variáveis quantitativas (valor_01, valor_02 …) que não são identificadas nominalmente.
Tamanho das partições do Cluster
cl$size
## [1] 19 719 1103 1712 308 966 145
fit <- kmeans(x, nrCluster)
clusplot(mydata, fit$cluster, color=TRUE, shade=TRUE, labels=4, lines=0,main = "Clusters e seus centroides",xlab = "Componente 1", ylab = "Componente 2")
Na abordagem Model-based clustering, cada componente de uma densidade de distribuição finita é geralmente associado a um grupo ou cluster. A maioria das aplicações assume que todas as densidades de componentes surgem da mesma família de distribuição paramétrica, embora isto não necessite ser o caso em geral. Um modelo popular é o modelo de distribuição gaussiana (GMM), que assume uma distribuição gaussiana (multivariada).
As abordagens Model-based assumem uma variedade de modelos de dados e aplicam a estimativa de máxima verossimilhança e os critérios de Bayes para identificar o modelo e o número de clusters mais prováveis. Especificamente, seleciona o modelo ótimo de acordo com BIC para EM inicializado por agrupamento hierárquico para modelos de distribuição Gaussiana parametrizada. Uma escolha do modelo e o número de aglomerados com o maior BIC
library(mclust)
fit <- Mclust(mydata)
plot(fit, what = "BIC", ylim = range(fit$BIC[,-(1:2)], na.rm = TRUE),
legendArgs = list(x = "bottomleft"))
Na chamada de função Mclust() acima são fornecidos a matriz de dados, o número de mix de componentes e a parametrização de covariância, todos são selecionados usando o critério de informação bayesiano (BIC - Bayesian Information Criterio). Um resumo mostrando os três modelos e um gráfico BIC para todos os modelo obtidos. No último gráfico, ajustamos o intervalo do eixo y para remover aqueles com valores BIC mais baixos. Há uma indicação clara do mix de três componentes com covariâncias com formas diferentes mas com o mesmo volume e orientação (EVE).
plot(fit, what = "class", ylim = range(fit$classification[,-(1:2)], na.rm = TRUE))
Na abordagem baseada em modelos de clustering, cada componente de uma densidade de distribuição finita é geralmente associada a um grupo ou cluster. A maioria das aplicações assume que todas as densidades de componentes surgem da mesma família de distribuição paramétrica, embora isto não necessite ser o caso em geral.
plot(fit, what = "density", ylim = range(fit$density[,-(1:2)], na.rm = TRUE))
Métodos eficientes de redução de dimensionalidade de dados representados em elevada dimensão são importantes, não apenas para viabilizar a visualização de dados em dimensões adequadas para a percepção humana, como também em sistemas automáticos de reconhecimento de padrões, como por exemplo, na eliminação de características redundantes.
fit <- MclustDR(fit)
summary((fit))
## -----------------------------------------------------------------
## Dimension reduction for model-based clustering and classification
## -----------------------------------------------------------------
##
## Mixture model type: Mclust (VEV, 5)
##
## Clusters n
## 1 1748
## 2 1516
## 3 535
## 4 814
## 5 359
##
## Estimated basis vectors:
## Dir1 Dir2 Dir3 Dir4 Dir5 Dir6
## GEO 0.0103142 0.0074478 0.0480796 -0.0263028 0.0555660 -0.0062387
## Age -0.0169451 -0.0213950 -0.0810618 0.0491219 -0.0702363 -0.0178006
## Prof -0.0050287 0.0582136 -0.0278809 -0.0011183 0.0204411 -0.0029680
## Gen -0.0996525 -0.0109621 0.3449327 -0.0208172 -0.0083706 0.0949383
## EstCiv 0.0055935 0.0075406 -0.0424347 0.0067182 -0.0697623 0.1478316
## V1 0.9594256 0.4350126 0.0089625 -0.0955034 -0.3053487 -0.0565104
## V2 0.2333542 -0.6639350 -0.5057144 0.3712398 0.7727197 0.5226859
## V3 -0.0567645 -0.4617333 -0.6416044 -0.9168836 -0.0664486 -0.1380943
## V4 0.0641771 -0.2441064 0.3633991 0.0229856 0.5394382 -0.7685010
## Per -0.0855960 0.3051061 0.2646094 -0.0909490 0.0296978 0.2875939
## Dir7 Dir8 Dir9 Dir10
## GEO 0.0781829 0.9725227 -0.0655461 -0.03415242
## Age 0.0565990 0.0055365 0.0032773 0.00567979
## Prof 0.0043349 -0.0049332 0.0013313 0.00031692
## Gen -0.1811567 0.2177909 0.7176824 0.95550508
## EstCiv 0.6354878 0.0057611 0.0583708 -0.00345518
## V1 -0.0162160 0.0154558 -0.0011628 0.01811550
## V2 -0.2043603 0.0291874 0.0071586 -0.01597868
## V3 -0.0086525 -0.0067342 0.0053341 -0.00139126
## V4 0.6569252 -0.0306825 -0.0332107 0.02894703
## Per 0.2834626 -0.0678304 -0.6899508 0.29047549
##
## Dir1 Dir2 Dir3 Dir4 Dir5 Dir6 Dir7
## Eigenvalues 67213.536 25204.624 5417.156 1226.774 316.702 60.084 42.343
## Cum. % 67.556 92.889 98.333 99.566 99.885 99.945 99.988
## Dir8 Dir9 Dir10
## Eigenvalues 6.9924 4.6644 0.70707
## Cum. % 99.9946 99.9993 100.00000
No sentido horário, a partir do canto superior esquerdo: BIC, classificação, incerteza e densidade aplicada ao exemplo simulado univariável. No gráfico de classificação, todos os dados são exibidos na parte inferior, com as classes separadas mostradas em diferentes níveis acima.
OBS: Com a redução de dimensionalidade o tamanho das partições se alterou. O mesmo ocorre com a quantidade de partições. Isso é pertinente, já que dados irrlevantes (cuja variabilidade não descrevem o cluster) foram retirados. Sugerimos optar por essa abortdagem.
plot(fit, what = "pairs")
Onde a incerteza é mostrada usando uma escala de cinza com regiões mais escuras indicando maior incerteza. Ambos traçam os dados para as duas primeiras direções. O diagrama de contorno das densidades de mistura para cada cluster (esquerda) e limites de clusterização (à direita) para o conjunto de dados.
plot(fit, what = "boundaries", ngrid = 200)
Como escolher os algoritmos de clustering apropriados para seus dados?
Começamos por validação de cluster interno que mede a conectividade, largura da silhueta e índice de Dunn. É possível calcular simultaneamente essas medidas internas para múltiplos algoritmos de clustering em combinação com uma série de números de cluster.
library(clValid)
clmethods <- c("hierarchical","kmeans","pam")
mx<-as.matrix(x)
intern <- clValid(mx, nClust = nrCluster, clMethods = clmethods, validation = "internal",maxitems = length(x$V1))
summary(intern)
##
## Clustering Methods:
## hierarchical kmeans pam
##
## Cluster sizes:
## 7
##
## Validation Measures:
## 7
##
## hierarchical Connectivity 30.6127
## Dunn 0.0416
## Silhouette 0.4828
## kmeans Connectivity 81.2742
## Dunn 0.0249
## Silhouette 0.4598
## pam Connectivity 115.8413
## Dunn 0.0006
## Silhouette 0.3940
##
## Optimal Scores:
##
## Score Method Clusters
## Connectivity 30.6127 hierarchical 7
## Dunn 0.0416 hierarchical 7
## Silhouette 0.4828 hierarchical 7
plot(intern)
stab <- clValid(mx, nClust = nrCluster, clMethods = clmethods, validation = "stability", maxitems = length(x$V1))
optimalScores(stab)
## Score Method Clusters
## APN 0.06212925 hierarchical 7
## AD 4.62240341 pam 7
## ADM 1.42092985 pam 7
## FOM 2.56055350 pam 7
summary(stab)
##
## Clustering Methods:
## hierarchical kmeans pam
##
## Cluster sizes:
## 7
##
## Validation Measures:
## 7
##
## hierarchical APN 0.0621
## AD 11.5970
## ADM 1.8269
## FOM 2.6225
## kmeans APN 0.1412
## AD 6.0499
## ADM 2.9656
## FOM 2.5648
## pam APN 0.1389
## AD 4.6224
## ADM 1.4209
## FOM 2.5606
##
## Optimal Scores:
##
## Score Method Clusters
## APN 0.0621 hierarchical 7
## AD 4.6224 pam 7
## ADM 1.4209 pam 7
## FOM 2.5606 pam 7
plot(stab)
Com o objetivo de verificar a relação entre variáveis, nesta seção vamos usar a regressão linear. É chamada “linear” porque se considera que a relação da resposta às variáveis é uma função linear de alguns parâmetros. Os modelos de regressão que não são uma função linear dos parâmetros se chamam modelos de regressão não-linear. Sendo uma das primeiras formas de análise regressiva a ser estudada rigorosamente, e usada extensamente em aplicações práticas. Isso acontece porque modelos que dependem de forma linear dos seus parâmetros desconhecidos, são mais fáceis de ajustar que os modelos não-lineares aos seus parâmetros, e porque as propriedades estatísticas dos estimadores resultantes são fáceis de determinar.
A exploração deve ser realizada por todas as variáveis, o que inclui as variáveis qualitativas, o quanto essas categorias influenciam, ou são influenciadas por outras variáveis.O que queremos agora é saber quais variáveis são dependentes e quais são independentes.
Idade
Vamos tentar estabelecer alguma relação entre a idade e os valores do dataset. Primeiro criamos um modelo com essas variáveis.
model <- lm(mydata$Age ~ mydata$V1 + mydata$V2 + mydata$V3 + mydata$V4)
summary(model)
##
## Call:
## lm(formula = mydata$Age ~ mydata$V1 + mydata$V2 + mydata$V3 +
## mydata$V4)
##
## Residuals:
## Min 1Q Median 3Q Max
## -63.919 -6.846 -2.104 4.170 55.749
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 34.7072 0.1482 234.187 < 2e-16 ***
## mydata$V1 0.9328 0.1497 6.233 4.96e-10 ***
## mydata$V2 1.6783 0.1559 10.765 < 2e-16 ***
## mydata$V3 0.9030 0.1517 5.953 2.82e-09 ***
## mydata$V4 0.9531 0.1520 6.270 3.91e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 10.45 on 4967 degrees of freedom
## Multiple R-squared: 0.06407, Adjusted R-squared: 0.06331
## F-statistic: 85 on 4 and 4967 DF, p-value: < 2.2e-16
Observe os valores de p (p-value).
print(model$coefficients)
## (Intercept) mydata$V1 mydata$V2 mydata$V3 mydata$V4
## 34.7071601 0.9328208 1.6782666 0.9030071 0.9530671
Embora os valores de correlaçãos sejam baixos, o valor_02 parece ter uma melhor relação com a idade dos clientes, o mesmo não ocorre com os outros valores, apesar da probabilidade estar abaixo de 0.001.
O gráfico
maxV <- max(mydata$V1,mydata$V2,mydata$V3,mydata$V4)
maxAge <- max(mydata$Age)
plot(mydata$V1, mydata$Age, col = "blue", pch = 19,
ylim = c(0,maxAge), xlim = c(0,maxV), xlab = "Valores", ylab = "Idade")
par(new=TRUE)
legend("bottomright", xpd=TRUE, ncol=2, legend=c("V1", "V2", "V3", "V4"),
fill=c("blue", "red", "green", "orange"), bty="n", pch = c(19,24,22,19))
par(new=TRUE)
plot(mydata$V2, mydata$Age, col = "red", pch = 24,
ylim = c(0,maxAge), xlim = c(0,maxV), xlab = "Valores", ylab = "Idade")
par(new=TRUE)
plot(mydata$V3, mydata$Age, col = "darkgreen", pch = 22,
ylim = c(0,maxAge), xlim = c(0,maxV), xlab = "Valores", ylab = "Idade")
par(new=TRUE)
plot(mydata$V4, mydata$Age, col = "orange", pch = 19,
ylim = c(0,maxAge), xlim = c(0,maxV), xlab = "Valores", ylab = "Idade")
abline(model)
Tempo total de execução deste programa
end.time <- Sys.time()
time.taken <- end.time - start.time
time.taken
## Time difference of 12.42483 mins
.
–