Arborização e o preço dos imóveis em São Paulo

Parece meio óbvio, mas será que é possível verificar uma correlação positiva entre a arborização de um local e os preços dos imóveis?

Outro dia, ao ir almoçar sozinho debaixo de um belo sol ‘de rachar’, de camiseta preta e sem uma única sombra para me ajudar comecei a delirar divagar sobre os prós e contras daquele local de trabalho. Por conta do momento, a falta de arborização naquele local era um ‘contra’, mas em oposição um fator positivo era o custo do aluguel (entre outros).

Por sorte temos o site http://geosampa.prefeitura.sp.gov.br/ que disponibiliza dados gerais sobre a cidade, como arborização, por exemplo:

arvores

Com esses dados, agregados aos da prefeitura de tamanho das subprefeituras e distritos podemos calcular o número de árvores por km²

top10

Eu poderia parar a análise por aqui já que quem mora/conhece a cidade de São Paulo sabe que esses são os bairros mais caros, porém vale a pena dar uma olhada nos preço médio do m² e comparar com a arborização do local.

Usando dados do properatidata.cartodb.com de preço de m² por distrito é possível criar o gráfico abaixo:

modelo

Razoável…

Obviamente existem diversos fatores que contribuem para o preço de um imóvel e é bem possível que a arborização de um local tenha uma importância boa em um modelo para prever preços de imóveis, como um modelo mais complexo que criei há um tempo atrás…

https://dadosdadosdados.wordpress.com/2015/09/15/alugueis-em-sao-paulo-um-benchmark/

Advertisements

Deep Learning com CUDA em tempos de crise

‘Neural Networks are cool again’ – Autor desconhecido

De quando em quando aparece uma notícia de um algoritmo mirabolante capaz de fazer alguma façanha que ninguém achava possível, porém ultimamente essas notícias estão ficando cada vez mais consistentes

Recentemente o Google anunciou algo que os especialistas achavam que demoraríamos pelo menos mais 10 anos para alcançar. Eles conseguiram montar um algoritmo capaz vencer um dos maiores campeões de GO do mundo.

http://www.wired.com/2016/03/googles-ai-wins-first-game-historic-match-go-champion/

Eu não conhecia o jogo GO a fundo. Joguei algumas vezes na infância porque alguém tinha o tabuleiro, mas nunca dei muita bola; e uma coisa que me deixou impressionado com essa notícia é a complexidade do GO em relação a outros jogos como jogo da velha e Xadrez.

https://en.wikipedia.org/wiki/Game_complexity#Complexities_of_some_well-known_games

‘Game-tree complexity’:
– Jogo da Velha = 10^5
– Xadrez = 10^123
– GO = 10^360

Só para se ter uma ideia, quando o Deep Blue venceu o Kasparov, apesar de ter sido um grande feito na época, ele ‘apenas’ buscava a melhor sequencia de jogadas dentro de um banco de jogadas, ou, para quem gosta de Excel, imagine um ‘super procv()’.

Já no caso do GO, isso ainda é uma impossibilidade por conta da complexidade do jogo, então o pessoal do DeepMind tentou outra abordagem onde eles ensinaram o computador a jogar GO olhando para milhares e milhares de partidas e até mesmo jogando contra outros computadores.

Usando a tecnologia
Diferente da postura usada pela IBM nos casos do Deepblue e mais recentemente do Watson, a tecnologia usada pelo Google está aí para brincarmos, principalmente agora que o Google abriu o código fonte das suas bibliotecas de Machine Learning

https://www.tensorflow.org/

Não é nada demais e é até inferior em alguns aspectos a bibliotecas que já estão por aí a algum tempo, como o Theano, mas o importante é que essas tecnologias estão aí, disponíveis para usarmos.

Na pratica:
As tecnologias estão disponíveis, mas um limitante para utiliza-las é o tempo de processamento,  por exemplo: Para um projeto que estou trabalhando eu preciso rodar diversos modelos e combina-los para fazer uma previsão. Uma das pernas desse processo é um modelo de Deep Learning, relativamente simples com 2 hidden layers, 2 dropout e alguns outros detalhes.

Considerando o problema inteiro o que eu teria que fazer é rodar esse modelo com 400 epochs, em um cross-validation de pelo menos 5 folds, com no mínimo 30 combinações diferentes de parâmetros. Com um tempo médio de processamento de 14.5s por epoch eu levaria 14.5 x 400 x 5 x 30 / 60 / 60 =~240 horas para rodar esse processo.

Processamento
Como as redes neurais são extremamente flexíveis e sua ‘profundidade’ pode afetar significativamente os resultados, chegamos em uma situação em que os recursos computacionais são limitadores na hora de criar redes maiores e mais complexas. Não são raros exemplos de pessoas que deixam suas redes treinando por dias ou semanas.

Uma solução para isso foi mudar o local de processamento das redes do processador para a Placa de vídeo pois como elas são feitas para trabalhar com gráficos pesados, são capazes de realizar muito mais operações por segundo chegando a ser até 20x mais rápidas que um processador equivalente.

Todo esse poder de processamento tem seu custo, é claro, geralmente a placa de vídeo é o componente mais caro de um PC e uma placa top não sai por menos de R$3.500,00

Ok, ok, estamos em crise e vai ser bem difícil arrumar R$10.000,00++ para comprar um PC novinho para brincar de Data Scientist, então o que dá para fazer?

CUDA on a Budget
CUDA é a Tecnologia usada pelas placas de video Nvidia que se comunica com as bibliotecas de Deep Learning (Theano ou Tensorflow) para mandar os cálculos para a GPU, então teoricamente qualquer PC com uma placa de vídeo que suporte CUDA pode ser usada para treinar uma rede Neural.

Tenho um notebook da Asus que por acaso vem com uma placa integrada GeForce 740m, que é uma porcaria de uma placa (do ponto de vista de um Gamer), mas é o que tem para hoje.

Instalando o CUDA no Linux
Eu já tinha visto diversos comentários sobre o quão chato é fazer esse troço funcionar, mas nada como testar na pele para ver como é.

Vou passar o passo a passo, mas já aviso que coisas podem ‘quebrar’ e isso vai variar por sistema, versão, hardware e etc, então tenha paciência, vai funcionar, mas nunca na primeira tentativa.

  • Faça o download do driver em:
    https://developer.nvidia.com/cuda-downloads
  • Vá para o terminal com CTRL+ALT+F1
  • Pare o ‘X server’, no meu caso ‘sudo service mdm stop’
  • sudo init 3
  • sudo sh cuda_7.5.18_linux.run

A partir de agora é só seguir a instalação e adicionar algumas linhas no .bashrc

export CUDA_HOME=/usr/local/cuda-7.5
export LD_LIBRARY_PATH=${CUDA_HOME}/lib64

PATH=${CUDA_HOME}/bin:${PATH}
export PATH

Obviamente, no meu caso a instalação do Driver de vídeo falhou, tentei de tudo e não deu certo e acabei instalando pelo Driver Manager do Linux mesmo (versão 352.63).

Como eu uso o Theano como motor eu precisei configura-lo para aceitar minha GPU, as instruções não são muito claras, mas eu criei um arquivo no root chamado ‘.theanorc’ com o seguinte conteúdo:

[cuda]
root = /usr/local/cuda-7.5

[global]
floatX = float32
device = gpu0

Isso deve ser o suficiente, com exceção de um detalhe que está marcado como Bug no site da Nvidia, mas eu não consegui resolver que é o seguinte, independente do programa que você usar (R ou Python) você deve abri-lo como administrador (sudo), caso contrário ele não reconhece a placa.

Resultados:
Na média, por epoch, consegui obter desta plaquinha uma velocidade 6x mais rápida para treinar minha network. Para diminuir o tempo de processamento de 10 dias para um pouco menos de 2 realmente fez valer a pena o trabalho com a instalação do CUDA.

chart1

Aquilo que não sei

Eu estava lendo o livro Rise of the “Robots: Technology and the Threat of a Jobless Future” e logo no começo o autor começa com a argumentação de que a produtividade do trabalho vem crescendo continuamente ao longo dos anos após a segunda guerra mundial.

us_productivity_and_real_wages

https://en.wikipedia.org/wiki/Workforce_productivity

Olhando para o gráfico, especialmente para a curva de produtividade (a de salários é um assunto para um outro momento) e pensando na maneira como trabalhamos hoje em dia é até difícil imaginar como era trabalhar na era ‘pre-Google’.

Vamos imaginar uma situação, você está em 1995 e precisa montar um report com alguns gráficos para seu chefe, porém você gostaria que os gráficos compartilhassem os eixos, mas você não faz ideia de como fazer isso. Você tinha basicamente três opções:

  • Perguntar para seus colegas de trabalho
  • Encontrar um livro
  • Tentativa e erro

Com um pouco de sorte existe um colega mais experiente ou você já possui um livro com a solução, mas a probabilidade era pequena, principalmente para problemas específicos.

Hoje em dia, por outro lado:

“matplotlib subplot”

O exemplo acima parece irrelevante, mas a quantidade de vezes que isso acontece é gigantesca, vou exemplificar:

Google Search History

O Google disponibiliza o histórico das suas pesquisas em:

https://history.google.com/history/

Nos últimos 1.000 dias eu realizei pouco mais de 17.000 pesquisas!!! E os sites mais acessados foram:

  1. stackoverflow.com
  2. en.wikipedia.org
  3. cran.r-project.org
  4. steamcommunity.com
  5. http://www.gamefaqs.com

Trabalho e jogos basicamente, porém é possível fazer o download de todas essas pesquisas, então é possível ir mais longe. Vamos analisar o número de pesquisas por dia da semana:

f1

f2

Bem, nenhuma novidade aqui, o número de pesquisas de Sábados e Domingos é menor e ao longo do mês as pesquisas são consistentes com exceção do dia 31, mas isso era esperado.

f3

A utilização desde 2013 aumentou consideravelmente, não só eu já utilizo bastante o Google como essa dependência tem aumentado ao longo do tempo.

Por fim, selecionei alguns dos termos que mais pesquisei para compara-los no tempo e o gráfico abaixo é o mesmo que dei como exemplo acima, onde eu apanhei um pouco para montar um gráfico que compartilhasse os eixos no Python (como vocês podem ver, eu só comecei a estudar Python recentemente)

f4

Alguns pontos são interessantes.

  • Wikipedia é uma constante em nossas vidas
  • Meu interesse pelo R provavelmente cresceu na mesma velocidade que a adoção do pacote ‘Dplyr’, que foi lançado no começo de 2014, até lá eu só o utilizava para rodar alguns modelos, mas não para ‘brincar’ com dados
  • Ultimamente, por conta do trabalho, tenho substituído o R pelo Python
  • As pesquisas sobre jogos costumam ter a mesma duração que meu interesse sobre eles e olhando para esse gráfico eu joguei muito mais Dota do que eu gostaria de admitir.
  • Apesar de parecer, não há correlação entre as pesquisas de jogos e trabalho, eu só não incluí mais gráficos…

 

 

Benchmarking CSV vs Parquet

Nos dias de hoje, provavelmente o formato mais utilizado para troca de dados é o CSV (Comma-separated values) e embora aqui no Brasil estejamos mais acostumados com a separação por ponto e vírgula me estranha muito um formato que existe desde a década de 70 perdurar até hoje.

Não dá para reclamar muito do bom e velho CSV, afinal é bem melhor do aquelas bases de dados do governo com separação fixa (por número de caracteres) ou as malditas planilhas de Excel.

Deve existir algo melhor, tem que existir algo melhor!

Em 2010 alguns caras do Google publicaram um artigo propondo uma nova forma de analisar dados chamada ‘Dremel’ onde segundo eles:

“Dremel is a scalable, interactive ad-hoc query system for analysis of read-only nested data. By combining multi-level execution trees and columnar data layout, it is capable of running aggregation queries over trillion-row tables in seconds. The system scales to thousands of CPUs and petabytes of data, and has thousands of users at Google. In this paper, we describe the architecture and implementation of Dremel, and explain how it complements MapReduce-based computing. We present a novel columnar storage representation for nested records and discuss experiments on few-thousand node instances of the system.”

Em outras palavras, Dremel é um sistema de consultas de bases gigantescas com seu formato em colunas e como sendo complementar a computação baseada em Map-reduce.

Bacana, mas eles não publicaram o código fonte, então os caras do Twitter juntamente com a Cloudera criaram em 2012 algo similar baseado no paper do Google chamado “Parquet” o qual em abril de 2015 se tonou um ‘Top Level Project’ na Apache foundation.

A ideia mesma, armazenar os dados em colunas de forma que nas consultas aos dados somente as colunas necessárias sejam escaneadas, isso trás basicamente alguns benefícios:

1 – Como somente as colunas necessárias são escaneadas as consultas tendem a ser muito mais rápidas

2 – É possível otimizar o fluxo de dados entre o processador e a memória (http://www.cidrdb.org/cidr2005/papers/P19.pdf)

3 – Os dados são mais facilmente comprimidos, pois os dados em forma de colunas são mais semelhantes entre si do que em linhas, imagine que você tem uma base de  7.000.000 de linhas, mas a coluna de UF tem 20 e poucos valores possíveis, sexo tem dois ou três valores, cor/raça tem meia dúzia e etc.

4 – Os dados em ‘Parquet’ já vem mapeados. Em um CSV nunca sabemos que tipo de valores estão contidos em uma coluna, se são texto, números, fatores, datas etc, no Parquet os dados já vem mapeados o que facilita demais! Isso me lembra a criação do MP3, que diferente do CD cada MP3 contém dados sobre o seu álbum como o artista, nome do álbum e até onde está a capa do álbum.

Não vou entrar muito em detalhes, caso contrário o post vai ficar muito longo, mas deixo a apresentação para quem tiver curiosidade.

 

E que vantagens isso trás?

Aproveitando meus posts anteriores onde já utilizo as bases de dados do Enem resolvi testar o formato para ver se realmente temos alguma vantagem sobre o csv.

O teste é simples, para diferentes tamanhos de amostra de dados do ENEM  (10.000 , 50.000, 100.000, … 300.000 linhas) faço a seguinte consulta:

Qual o percentual de alunos que não realizou a prova de Matemática por estado?

A mesma consulta é feita de 3 formas diferentes, ‘read.csv’ padrão do R, ‘read.df’ ainda lendo o CSV, mas usando uma instancia local do Spark e por fim via ‘parquetFile’ também no Spark. (o código está no final do post).

O resultado pode ser visto nos gráficos abaixo:

Rplot01

 

Até que funciona bem! Eu já sabia que o ‘read.csv’ do R era ineficiente, mas não tanto!

Rplot03

 

Comparando o ‘read.df’ e o Parquet ambos rodando no Spark, conforme aumentamos o tamanho do arquivo aumentamos a diferença de tempo entre eles, sendo que para um arquivo de 300.000 linhas ler um Parquet já é 15x mais rápido.

Rplot02

Por fim, como ‘bônus’ podemos ver que o tamanho de um arquivo CSV tende a ser um pouco mais de 8x maior do que o de um Parquet. Os dados do ENEM, por exemplo, vão de 5 e poucos GB para pouco mais de 700 megas.

 

Conclusão e próximos passos

Ao se tratar de ‘Big Data’ acredito que não temos o que discutir, vale a pena gastar um tempo convertendo as bases para Parquet para que estas sejam utilizadas depois com mais eficiência.

 

Já para ‘Small Data’, temos uma questão importante de como o R usa a memória, pois os arquivos são carregados na RAM e depois podem ser manipulados de maneira muito eficiente com o Dplyr. Já no Spark temos que alocar os arquivos na memória (comando ‘cache()’), na prática acaba dando no mesmo, porém pouca coisa é mais eficiente (e elegante) que o Dplyr.

Apesar dos ganhos serem significativos em termos percentuais não estou certo se vale a pena gastar tempo convertendo arquivos para um ganho de 30-40 segundos.

Algumas perguntas que precisam ser respondidas ainda:

  • Será que converter os arquivos ‘grandinhos’ para Parquet, carrega-los via Spark e usar ‘collect()’ para trazer para converter para Data.Frame do R e depois usar o Dplyr para manipula-los é uma opção?
  • Qual a diferença de tempo gasto em diversas operações no Spark-cache() vs R-Dplyr?
  • Usar o Spark com outro formato de dados é uma pentelhação, mas será que não vale a pena usar o mesmo processo para trabalhar com qualquer tipo de dados em qualquer lugar?

Código

rm(list=ls())
library(dplyr)
library(tidyr)
#library(ggplot2)

# Set this to where Spark is installed
Sys.setenv(SPARK_HOME="/home/sandor/spark-1.5.2-bin-hadoop2.6")
# This line loads SparkR from the installed directory
.libPaths(c(file.path(Sys.getenv("SPARK_HOME"), "R", "lib"), .libPaths()))
library(SparkR)


i <- 50
tCSV <- NULL
tPARQ <- NULL
size <- NULL
tNORM <- NULL
for (i in 1:25){
sc <- sparkR.init(sparkPackages = 'com.databricks:spark-csv_2.10:1.3.0')
sqlContext <- sparkRSQL.init(sc)
caminho <- '/home/sandor/Enem/2013/DADOS/MICRODADOS_ENEM_2013.csv'

####################
t1 <- Sys.time()
sample <- read.csv(caminho, sep = ';', nrows = i*12000)

sample %>%
dplyr::group_by(UF_PROVA,IN_PRESENCA_MT) %>%
summarise(N=n()) %>%
spread(IN_PRESENCA_MT, N) %>%
dplyr::mutate(Faltas=`0`/(`1`+`0`)*100) %>% dplyr::select(UF_PROVA, Faltas)

#nrow(sample)
tNORM[i] <- as.numeric(Sys.time()-t1)
####################

write.table(sample, '/home/sandor/Enem/2013/DADOS/MICRODADOS_ENEM_2013_SAMPLE.csv', sep=';', row.names = F)

####################
t1 <- Sys.time()
ENEM <- read.df(sqlContext, path='/home/sandor/Enem/2013/DADOS/MICRODADOS_ENEM_2013_SAMPLE.csv',
source = "com.databricks.spark.csv", inferSchema = "false", delimiter=';', header='true')
#count(ENEM)
registerTempTable(ENEM, "EE")
resCSV <- sql(sqlContext, "SELECT UF_PROVA, count(case IN_PRESENCA_MT when '0' then 1 else null end)/count(IN_PRESENCA_MT)*100 as Faltas
FROM EE
GROUP BY UF_PROVA")

collect(resCSV)
tCSV[i] <- as.numeric(Sys.time()-t1)
####################

unlink('/home/sandor/R_files/Parquet/base_1', force=T, recursive = T)
saveAsParquetFile(ENEM, '/home/sandor/R_files/Parquet/base_1')

####################
t1 <- Sys.time()
PARQ <- parquetFile(sqlContext,'/home/sandor/R_files/Parquet/base_1')
#count(PARQ)
#cache(PARQ)

registerTempTable(PARQ, "PQ")
resPQ <- sql(sqlContext, "SELECT UF_PROVA, count(case IN_PRESENCA_MT when '0' then 1 else null end)/count(IN_PRESENCA_MT)*100 as Faltas
FROM PQ
GROUP BY UF_PROVA")

collect(resPQ)
tPARQ[i] <- as.numeric(Sys.time()-t1)
####################

setwd('/home/sandor/R_files/Parquet/base_1')
sPARQ <- sum(file.info(list.files(".", all.files = TRUE, recursive = TRUE))$size)
sCSV <- file.size('/home/sandor/Enem/2013/DADOS/MICRODADOS_ENEM_2013_SAMPLE.csv')

size[i] <- sCSV/sPARQ
sparkR.stop()

}

Analisando o ENEM

Nos últimos posts tenho explorado maneiras de se trabalhar com Big Data no R. Apesar de nova, a integração do R com o Apache Spark é bem razoável e a implementação do Hive funciona bem, apesar de alguns percalços ao criar grupos, travamentos aleatórios e algumas configurações pentelhas.


Como utilizei os dados do Enem para montar o post anterior, ao olhar o dicionário de dados acabei pensando em algumas perguntas que poderiam ser respondidas com dados, entre elas:

– qual a relação entre renda e a nota?
– qual a relação entre os anos de estudo dos pais e a nota do aluno?

Essas duas são bem básicas e sua resposta é esperada…

Outras perguntas tem a ver com o sexo e cor/raça dos alunos ajustadas pela renda.

Por fim é possível descobrir quais questões os alunos mais erraram/acertaram.

1 – Renda

Renda

O primeiro gráfico era esperado, quanto maior a renda da família, maior a média da nota.

2 – anos de estudo do pai

Anos_Estudo

Mesma coisa do primeiro gráfico, quanto mais anos de estudo tem o pai, maior a média da nota.

Obviamente, quanto mais anos de estudo, maior a renda, ou será que é o contrário?*

* – essa discussão vai ficar para uma próxima oportunidade

3 – sexo

Participei do Big Data week no final de outubro e somente um palestrante era mulher, que por coincidência apresentou um gráfico mostrando que 12% dos programadores Java são mulheres.

Esse número é bem baixo, mas não é muita novidade para o pessoal de exatas e até mesmo para o pessoal da Economia.

Minha hipótese era de que essa decisão é tomada na adolescência, levado talvez por uma deficiência nos cursos de matemática.

O gráfico abaixo mostra a distribuição das notas de matemática entre homens e mulheres. De fato a mediana das notas das mulheres foi menor mas nada tão alarmante que fosse capaz de desencorajar uma pessoa de fazer um curso.

sexo
4 – cor/raça

Na minha época de cursinho no Anglo Tamandaré (próximo o bairro da liberdade em São Paulo) tinha uma frase escrita em todas as cabines do banheiro masculino:

– “Enquanto você está cagando tem um Japonês estudando”

Finalmente, depois de tantos anos, vou poder colocar esse dito popular a teste!

Ao agregar os números por cor e raça não conseguimos observar nada demais, porém, quando controlamos pela renda, assumindo que são estes os alunos competindo pelas melhores vagas (ver gráfico 01) a mediana da nota dos alunos que se classificam como “amarelos” é a maior de todas, nada muito significativo, mas maior de qualquer forma.

asia
Talvez aqui seja interessante fazer um teste de hipóteses para verificar se a nota é significativamente maior.

5 – perguntas mais difíceis.

Por fim, existe um campo na base com todas as respostas de cada aluno, bem como o gabarito, cruzei um com o outro para pegar os acertos e somei isso para pegar o número de acertos por questão, a questão com menor número de acertos foi a mais difícil.

Seguem as questões mais difíceis, eu apaguei a resposta, mas se você tiver curiosidade o nome da figura é a resposta:
MT_E CN_C CL_B CH_C

Microdados ENEM com R / Hadoop / Spark / Hive

A definição de Big Data que acho mais interessante é algo mais ou menos assim:

  • “Big Data são os dados que não cabem na memória RAM”

Quem desenvolve em R sabe que essa definição é perfeita…

O problema é que há uns 15 anos atrás HDs de 100GB de memória eram raros e de lá para cá essa capacidade de armazenamento aumentou muito. Atualmente HDs de 1TB são bem comuns, porém a velocidade com que os dados são lidos não aumentou na mesma velocidade. Então, para ler um HD inteiro, leva-se mais tempo hoje do que a 15 anos atrás.

Mapreduce

Até que alguém teve uma ideia muito simples que é ler dados de diversos HDs ao mesmo tempo, uma analogia interessante é:

Você tem que contar os livros de uma biblioteca, uma opção seria contar um a um até chegar no total ou chamar seus amigos e dizer:

  • “Ok, temos 8 corredores (MAP), cada um de vocês conta os livros de cada corredor(REDUCE) e me fala o total”

Atualmente a aplicação mais famosa de Mapreduce é o Hadoop, que é provavelmente o projeto de Open Source de maior impacto no mundo dos negócios até hoje.

O problema agora, como comentei no meu post anterior (https://dadosdadosdados.wordpress.com/2015/11/09/o-varian-pirou/ ), é que existe um gigantesco ecossistema de ‘coisas’ para estudar, aprender, usar e manter o que torna impraticável na ‘vida real’, ou vai demandar uma gigantesca dedicação.

Spark

SPARK

A partir de meados de 2015 o R foi integrado ao Spark, que é uma plataforma de computação em cluster que foi desenhada para ser rápida e genérica. Sua diferença básica com o Hadoop é que ele faz suas computações na memória (ram) ao invés de usar os discos (hd), isso permite a ele realizar cálculos que antes eram bem complicados de serem feitos em uma operação de Mapreduce comum.

Outro ponto importante é que ao invés de termos que focar em diversas tecnologias ao mesmo tempo, a filosofia do Spark é de prover um pacote completo para os analistas não terem que gastar tempo mantendo 15 sistemas ao mesmo tempo, o que diminui drasticamente os custos.

No final das contas, com o Spark conseguimos nos aproximar muito do modelo proposto pelo Varian para análise de dados, com um overhead menor, seja em custos de se manter sistemas, em horas de aprendizado.

Simulando um ambiente

Para efeitos de treino/testes, é possível simular um ambiente de Spark em apenas 01 cluster, ou seja, ao invés de distribuir os cálculos em várias máquinas, usamos somente nosso computador. Não é a coisa mais eficiente do mundo (computação distribuída em 01 computador não é distribuída), mas é possível desenvolver seu código nesse cluster único e depois subir para a nuvem quando ele estiver pronto.

Pré requisitos:

1 – Virtualbox
https://www.virtualbox.org/wiki/Downloads

Eu costumo configurar essas máquinas virtuais com 50GB de HD, 8GB de RAM e 64MB de memória de vídeo, essas configurações devem variar dependendo da máquina que você vai rodar isso.

No meu notebook eu uso 2GB de RAM e 12MB de vídeo somente.

2 – Linux no virtualbox – A versão que uso é o Mint (Rafaela)
http://linuxmint.com/

3 – Extension pack do Virtual box
https://www.virtualbox.org/wiki/Downloads

No linux:
4 – Java
$ sudo apt-get install openjdk-7-jdk
$ export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64

5 – Hadoop

https://hadoop.apache.org/docs/r2.5.2/hadoop-project-dist/hadoop-common/SingleCluster.html

6 – Hive
https://cwiki.apache.org/confluence/display/Hive/AdminManual+Installation#AdminManualInstallation-InstallingHive

7 – Spark – prestar atenção na versão do Hadoop, a versão pre-built serve
http://spark.apache.org/downloads.html

8 – R – Note que para o R, Trusty é a versão do Linux que estou usando, se você estiver usando uma versão diferente, é possível que você tenha que mudar o final do endereço

Adicionar em: /etc/apt/sources.list
deb https://cran.rstudio.com/bin/linux/ubuntu trusty/

Atualizar e instalar:
$ sudo apt-get update
$ sudo apt-get install r-base

$ R para checar a versão

9 – R-Studio
https://www.rstudio.com/products/rstudio/download/

Um caso real – Microdados ENEM

A pior coisa de usar tutoriais e demos é que tudo sempre dá certo, é uma beleza! O próprio tutorial do Spark usa um data.frame de umas 600 linhas, que meigo…

Eu encorajo a todos a tentarem algo diferente, para ver os problemas e dificuldades e o exemplo que vou mostrar aqui é dos microdados do ENEM.

É realmente uma pena a demora do INEP a divulgar os dados, pois os dados mais novos disponíveis são de 2013, mas é o que tem para hoje…

http://portal.inep.gov.br/basica-levantamentos-acessar

O legal dessa base de dados é que ela tem 5.1GB de dados, com 7.173.564 linhas e 166 colunas, o suficiente para travar minha máquina virtual, mesmo que ela possua 8GB de RAM, então ‘tecnicamente’ podemos considera-la como ‘BIG Data’.

Para essa análise quero comparar as notas das provas entre os estados do Brasil.

Rodando

O sparkR é realmente bacana, você basicamente precisa de dois comandos para começar a usar:

sc <- sparkR.init(master = 'local')
hiveContext <- sparkRHive.init(sc)

O primeiro comando inicia um contexto Spark e o segundo cria um contexto HIVE desse primeiro.

Depois, e é aí que as coisas começam a ficar diferentes de um exemplo comum, na hora de montar uma query no HIVE, você tem que declarar as colunas, o que é lindo quando você tem duas ou três, mas quando você tem 166 no caso do ENEM, você apela…

st <- rep('string', length(colnames(sample)))

st[match('NOTA_CN' ,colnames(sample))] <- 'int'
st[match('NOTA_CH' ,colnames(sample))] <- 'int'
st[match('NOTA_LC' ,colnames(sample))] <- 'int'
st[match('NOTA_MT' ,colnames(sample))] <- 'int'

Como queremos analisar somente as notas médias, vou forçar todos os campos a serem strings e somente a notas como números inteiros.

c1 <- paste(colnames(sample), st)
campos <- paste(c1, sep=',',  collapse = ', ')
cTable <- paste("Create external table src1 (", campos, ") row format delimited fields terminated by ';' SET skip.header.line.count = 1 ")

sql(sqlContext, "DROP TABLE src1")
sql(sqlContext, cTable)
sql(sqlContext, "LOAD DATA LOCAL INPATH '/home/sandor/Enem/2013/DADOS/MICRODADOS_ENEM_2013.csv' OVERWRITE INTO TABLE src1")

Agora é só agrupar os dados em um select comum

results1 <- sql(sqlContext, "SELECT UF_RESIDENCIA,
                avg(NOTA_MT) as AVG_MT,
                avg(NOTA_CN) as AVG_CN,
                avg(NOTA_CH) as AVG_CH,
                avg(NOTA_LC) as AVG_LC
                FROM src1 GROUP BY UF_RESIDENCIA")

Para transformar em data.frame do R usamos o comando ‘collect()’

tabela <- collect(results1)

Temos que pro fim ‘stopar’ o Spark

sparkR.stop()

O resultado

Agora, pegamos a nota média por estado por prova e subtraímos da média total para comparar as notas entre provas e estados.

Note que:

MT = Nota da prova de Matemática

LC = Nota da prova de Linguagens e Códigos

CH = Nota da prova de Ciências Humanas

CN = Nota da prova de Ciências da Natureza

plot of chunk unnamed-chunk-8

A PNAD, os DVDS e as Florestas (Aleatórias)

Motivação

Em jun/15, em uma conversa de ‘boteco’ alguém sugeriu que eu comprasse um DVD da Galinha pintadinha para minha filha recém-nascida e a primeira coisa que pensei era: – ‘Ótimo, aí eu penduro o DVD no móbile dela’, pois não tenho onde reproduzir esse troço…

Isso me lembrou a época de ouro das locadoras de vídeo, onde era praticamente um passeio de família ir a locadora pegar um ou dois filmes para assistir no fim de semana e gigantes como a Blockbuster quando chegaram ao Brasil mudaram a cara deste mercado para sempre, da locadora de porta de garagem para algo grandioso, cheio de opções e principalmente sem filas de espera…

Porém no final de 2013 foi oficialmente declarada a morte da Blockbuster link, uma morte mais do que esperada, mas ainda assim o fim de uma era…

…Com isso, vamos fazer uma viagem no tempo, pegar os dados da PNAD 2013 e tentar responder a seguinte pergunta:

“Com tantas maneiras de assistir filmes, e as ruínas das locadoras espalhadas pelo país, o que leva uma família a ter um aparelho de DVD (em 2013)?”

Vamos tentar montar um modelo para explicar isso…

A PNAD

Para quem gosta de trabalhar com dados, o IBGE é um prato cheio. A PNAD é uma pesquisa divertidíssima onde temos uma bela amostra de dados (longe de ser perfeita, mas boa para algumas análises) e perguntas o suficiente para a realização das mais variadas análises.

Estávamos passando pelos dicionários da pesquisa de 2013 recentemente e encontramos a seguinte questão:

  • ‘Tem aparelho de DVD’?

Os dados

Não vamos nos preocupar em explicar como extraímos os dados da PNAD, as instruções estão no site do IBGE neste link e como isso por si só já dá um belo trabalho, talvez um dia no futuro escreveremos um post sobre como fazer isso.

A última PNAD anual foi realizada em 2013, então é essa pesquisa que vamos utilizar. Atualmente existe a PNAD contínua, mas para o efeito que queremos capturar nesse exercício a de 2013 está mais do que ótima.

Factor engineering

Essa parte é relativamente trabalhosa, pois temos que:

  1. Transformar variáveis numéricas em fatores. Olhar para o dicionário da PNAD é um exercício de paciência. Por exemplo:

‘Tem televisão em cores?’

2 = SIM
4 = NÃO

‘Tem aparelho de DVD?’

1 = SIM
3 = NÃO

PORQUEEEEEEEEEEEE?????

É muito difícil normalizar isso? Que tal 1=sim e 0=não? Ou, para facilitar, 2 = sim, 4 = não….sempre!

  1. Criar algumas variáveis que vão ajudar na nossa modelagem, como por exemplo ‘Acesso_INTERNET’ que combina as questões de banda larga e discada em uma variável
  2. Vamos usar os dados do questionário dos domicílios, mas no dicionário das pessoas vamos criar variáveis como EH_FILHO que é igual ao número de filhos de uma família, EH_FILHO_6 que é igual a quantidade de filhos menores do que 6 anos e FAIXA_IDADE_FILHO que é a média de idade dos filhos de um domicílio

Os datasets

A primeira coisa a fazer é criar os datasets de treino e teste, só que temos um problema, considerando que a amostra da PNAD domicílios é de mais de 148.000 observações onde 116.000 são válidas e cada um tem seu peso, na teoria teríamos que repetir cada domicílio um número de vezes igual ao seu peso, ou seja, se o peso médio é 560, temos mais de 64.000.000 domicílios.

O dataset do nosso modelo vai consistir em 100.000 observações selecionadas aleatoriamente do universo de 64.000.000

DICA DO DIA: Nunca tente criar um vetor de 64.000.000 de observações no R e selecionar 100.000 linhas aleatoriamente!!!

Depois de travar o PC mudamos de estratégia…

Como?

Vamos assumir um universo de 3 domicílios com pesos 1.000.000, 50.000.000 e 13.000.000, criamos um vetor de pesos acumulados, 1.000.000, 51.000.000 e 64.000.000, fazemos 100.000 sorteios aleatórios de 1 a 64.000.000 e simplesmente contamos o número de itens no vetor de pesos acumulados menores do que o sorteio mais 1, por exemplo:

Sorteio: 10; número < 0; posição = 1
Sorteio: 1.100.000; numero < 1; posição = 2
Sorteio: 64.000.000; numero < 2; posição = 3

Isso faz com que a probabilidade se selecionarmos um domicílio seja igual seu peso no total.

Treino e teste

Agora que temos as 100.000 observações aleatórias, vamos dividir em dois datasets, um para treinar o nosso modelo e outro para testar seus resultados, faremos isso com a razão de 70% treino e 30% teste

 

O modelo

Para o modelo temos um problema de classificação, ou seja, para um certo grupo de inputs queremos uma resposta 0 ou 1 para classificar um domicílio como ‘Tem DVD’ ou ‘Não tem DVD’, esses inputs são algumas variáveis socioeconômicas dos domicílios e dos seus habitantes.

library(randomForest)
modelFit <- randomForest(Tem_DVD ~.,
                         data=train,
                         do.trace=T, 
                         ntree=200,
                         importance=TRUE,
                         mtry=10)
plot(modelFit,main='randomForest error rate')

plot of chunk unnamed-chunk-4

O pessoal costuma ir para o Overkill rodando milhares de árvores, mas a verdade é que não precisamos de tantas para estabilizar o erro, além disso demora uma eternidade! É possível ver pelo gráfico acima que depois de 100 árvores o erro já está mais do que controlado e o ganho é marginal.

Mostrando o pau

Agora que matamos a cobra, vamos aplicar nosso modelo no dataset de testes e ver se o mesmo ficou aceitável ou se somente capturou o ruído do dataset de treino.

pred <- predict(modelFit,newdata=test)
CM <- confusionMatrix(test$Tem_DVD, pred)
CM
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction     0     1
##          0  5573  2835
##          1  1578 20013
##                                           
##                Accuracy : 0.8529          
##                  95% CI : (0.8488, 0.8569)
##     No Information Rate : 0.7616          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.6179          
##  Mcnemar's Test P-Value : < 2.2e-16       
##                                           
##             Sensitivity : 0.7793          
##             Specificity : 0.8759          
##          Pos Pred Value : 0.6628          
##          Neg Pred Value : 0.9269          
##              Prevalence : 0.2384          
##          Detection Rate : 0.1858          
##    Detection Prevalence : 0.2803          
##       Balanced Accuracy : 0.8276          
##                                           
##        'Positive' Class : 0               
## 

TMI [Too much information] – Mais para frente faremos um post explicando detalhes do que é cada um desses resultados, por enquanto vamos focar em duas coisas:

Accuracy : 0.8529

O modelo tem uma precisão de 85%, ou seja, com base em variáveis socioeconômicas é possível prever com 85% de precisão se um domicílio tem ou não DVD. O intervalo de confiança é bem pequeno (95% CI : (0.8488, 0.8569)), o que é bom.

CM$table
##           Reference
## Prediction     0     1
##          0  5573  2835
##          1  1578 20013

Temos dois tipos de erro, quando a referência diz que um domicilio tem um DVD e o modelo diz que não e quando a referência = não e o modelo = sim. Como diria um colega, se o modelo diz algo e a realidade diz outro é a realidade que tem um problema…

Importância das variáveis

plot of chunk unnamed-chunk-7

Um negócio bacana das florestas é poder observar a importância das variáveis e para esse modelo Renda vence de longe como sendo a mais importante, depois a UF (afinal estamos no Brasil e Renda+UF explicam praticamente tudo!) e só depois a média de idade dos filhos.

Uma coisa interessante é que as variáveis de filhos que criamos para este modelo (EH_FILHO = número de filhos no domicílio e EH_FILHO_6 = Número de filhos com 6 anos ou menos) não fazem muita diferença no modelo…

Pelo visto ninguém vai comprar um aparelho de DVD só pela Galinha Pintadinha…