Introdução

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

Preparação do ambiente para execução

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

Uma função para validar os pacotes necessário se estão instalados

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]) 

Uma Função para calcular a idade dos cliente de acordo com sua data de nacimento

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
}

Verificando se os pacotes necessários estão instalados

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)

Carrega os pacotes necessários para a execução

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)

Carga do Dataset

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)

Limpeza e arrumação dos dados

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

Clusterizando

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.

Modelo hierárquico

Conjunto hierárquico ascendente de um conjunto de variáveis.

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]

Número de cluster encontrados, ou aglomerações

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)

CLuster Dendrogram

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

Índice Rand ajustado

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.

Particionamento

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.

K-Means Clustering with k clusters

PCA - Principal Component Analisys

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")

Model-based Clustering

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).

BIC

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).

Classificação

plot(fit, what = "class", ylim = range(fit$classification[,-(1:2)], na.rm = TRUE))

Densidade

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))

Redução de dimensionalidade

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.

Clustering

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)

Validando o melhor algorítmo

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)

Inferência Estatística

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.

Apenas um exemplo

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

.

The Scientist