Cachorros artificiais: agentes de agent-based modeling usando R (ABM – Parte 1)

(Primeiro Post da série. Veja os outros: Parte 2, Parte 3, Parte 4 e Parte 5)


robot-dog-meets-a-real-doberman-dog

Modelos baseados em agentes (ou Agent-Based Models – ABM) já foram um tópico discutido no blog outras vezes (por exemplo, aqui e aqui). Mas julgo que esse é um assunto “quente”, que sempre vai acabar voltando.

Para aqueles que não sabem ao que estou me referindo, vou dar uma ideia simples e rápida. Trata-se de simulações de algo como “sociedades artificiais” através de modelos computacionais. O propósito NÃO É construir a Matrix ou qualquer coisa do tipo… ao, contrário, é algo bem mais “humilde” e focado. Funciona assim: o pesquisador tem hipóteses sobre como indivíduos ou grupos se comportam, cria um ambiente simples que contém apenas algumas características fundamentais e indispensáveis para a caracterização de uma situação típica e então toca o play. Com isso, pode investigar se os mecanismos interacionais supostos de fato produzem, no nível macro ou agregado, os padrões efetivamente observáveis. Trata-se de um exercício lógico, antes de qualquer outra coisa.

Economistas neoclássicos, trabalhos que envolvem modelagem formal e escolha racional fazem exercícios lógicos de natureza semelhante o tempo todo: “suponha indivíduos homogêneos (idênticos em todas as suas características), com determinado tipo de preferência e orçamento; de fronte a um mercado que se lhes apresente certas quantidades e preços, eles se comportarão desta e daquela maneira, gerando um estado de equilíbrio tal e qual…”. No entanto, nem todo tipo de modelagem deve envolver escolha racional… e nem matemática. Certos problemas são complexos o suficiente para não permitir “forma fechada” (i.e. uma equação bem definida, cuja solução informa um resultado predito). Os ABMs entram em cena exatamente aí.


Um agente é uma entidade conceitual, que pode ser representada de diversas formas e nas mais diversas linguagens de programação. Mas duas propriedades fundamentais devem estar presentes: atributos e ações. Um indivíduo, por exemplo, tem diversas características: idade, peso, altura, raça, sexo, ocupação etc… Esses são os atributos. E também é capaz de agir no mundo de diversas formas: correr, gritar, socializar, procurar emprego etc. Um “agente”, nesse sentido a que me refiro, seria a representação computacional dos aspectos analiticamente relevantes de um indivíduo para determinados fins de pesquisa. Ou seja, ele não contém todos os atributos e ações possíveis de um indivíduo, mas apenas aqueles imprescindíveis para a análise. Isso vai ficar mais claro adiante.

O exemplo que vou dar será utilizando o R, minha “linguagem nativa” (que uso no dia a dia para fazer análises estatísticas). Mas reconheço que ele não é o melhor ambiente pra isso… Existem linguagens de programação específicas para ABM (a mais conhecida é a LOGO, que se tornou bastante popular devido ao software NetLogo, que implementa uma de suas versões). Também bastante utilizada é a linguagem Python — que é uma linguagem de programação completa (não específica para ABM) e tão flexível quando o R (e muito parecida, inclusive). ABMs escritos em Python têm scripts mais simples, diretos e rodam mais rápido; de acordo com minha experiência (mas esse último ponto é controverso).

As estruturas de dados mais simples e comuns no R são vetores, matrizes, listas e data.frames. Basicamente, todos os outros tipos de objetos existentes são algum tipo de combinação ou transformação desses… Além disso, os comandos que executamos também são objetos e são chamados funções. Criar um agente é guardar seus atributos em algum desses tipos de estrutura de dados e representar suas ações por funções. Uma forma conveniente de mesclar atributos e funções num único objeto-agente é fazer uso de um tipo de estrutura de dados pouco utilizada no R, chamada Reference Class (veja mais coisas sobre isso aqui).

Dou um exemplo. Desejamos representar um cachorro como agente. Os atributos relevantes serão a idade e a raça. E as ações possíveis serão latir e nos dizer qual é a sua raça e a sua idade, se lhe perguntarmos (sim! é um cachorro falante! e daí!?). Criaremos uma Reference Class do tipo “Cachorro”

Cachorro = setRefClass("Cachorro",
             fields = c("raca",
                        "idade"),

             methods = list(
                 late = function(){
                     print("Au! Au!")
                 },

                 diz_raca = function(){
                     print(paste("Eu sou um", .self$raca))
                 },

                 diz_idade = function(){
                    print(paste("Tenho", .self$idade,
                                "anos, o que significaria",
                                 7*.self$idade,
                                 "para um humano" ))
                 }
) )

Acredito que para quem conhece um pouco de R, mesmo sem jamais ter visto uma Reference Class antes, o procedimento acima é mais ou menos inteligível. Com o comando setRefClass estamos criando esse novo tipo de objeto — tipo que será chamado “Cachorro”. Ele terá dois atributos, cujos nomes estão declarados no argumento  fields (como vetores character). Depois especificamos as ações que os Cachorros são capazes de realizar: latir, dizer a raça e dizer a idade. Observem uma coisa importante, que é específica das Reference Classes: a expressão .self. Quando .self$raca estamos dizendo que o atributo raça está contido dentro do próprio objeto Cachorro, não é preciso procurá-lo no ambiente principal do R. Essa é a principal característica das Reference Classes: a capacidade de se auto-referenciar. Qualquer pessoa familiarizada com C++,  Java ou Python logo identifica que essa é uma das principais características de qualquer “linguagem orientada a objetos“. Essa expressão simplesmente significa que os objetos criados são auto-contidos: trazem consigo informações, dados e também os métodos para acessá-los, modificá-los e utilizá-los.

Tá… e agora? Agora que definimos as propriedades de um cachorro em abstrato, o próximo passo é efetivamente criar um (é como se tivéssemos definido a “idéia” de cachorro e agora fossemos de fato criar um “cachorro empírico”).

Jimmy = Cachorro$new(raca = "Vira-latas", idade = 4)

Acredito que o código acima seja mais o menos intuitivo. Mas é bom discuti-lo. Observem que estamos utilizando o $ para acessar uma função que está “dentro” do objeto Cachorro, chamada new(). Essa não era uma das ações definidas no argumento methods (late, diz_idade, diz_raca). Trata-se de uma função presente em todo e qualquer objeto do tipo Reference Class, que significa, em termos simples, “crie pra mim uma espécie ou instancia empírica dessa classe”. Informamos as características daquele exemplar particular de cachorro (Vira-latas, com 4 anos). E pronto, fiat Jimmy. Esse é o Jimmy “por dentro”:

jimmy1

Ele é capaz de latir, dizer a idade e a sua raça:

jimmy2

Vamos agora criar novamente a classe Cachorro, mas com duas novas capacidades de ação: dar a pata e abaixá-la. Além dessas duas novas funções, que deverão ser informada dentro da lista passada para o argumento methods, temos que também definir um novo atributo, o “status” da pata (i.e., se está levantada ou abaixada). Se já estiver levantada e pedirmos mesmo assim para que ele a levante, o cachorro vai nos informar. O mesmo se já estiver abaixada e ainda assim pedirmos a ele para abaixá-la.

Cachorro = setRefClass("Cachorro",
             fields = c("raca",
                        "idade",
                        "pata"),

             methods = list(
                 initialize = function(pata = 0, ...){
                     .self$pata = pata
                     callSuper(...)
                 },
                 late = function(){
                     print("Au! Au!")
                 },

                 diz_raca = function(){
                     print(paste("Eu sou um", .self$raca))
                 },

                 diz_idade = function(){
                    print(paste("Tenho", .self$idade,
                                "anos, o que significaria",
                                 7*.self$idade,
                                 "para um humano" ))
                 },

                 da_pata = function(){
                     if( .self$pata == 0 ){
                         .self$pata = 1
                         print("Levantei a pata")
                     }else{
                         print("Já te dei a pata, oras...!")
                     }
                 },

                 abaixa_pata = function(){
                     if( .self$pata == 0 ){
                         print("Minha pata já está abaixada!")
                     }else{
                         .self$pata = 0
                         print("Abaixei a pata")
                 }
}
) )

Observem que uma outra função teve que ser definida dentro de methods, denominada initialize. Essa função simplesmente especifica valores-padrão que serão passados para todos os agentes daquela classe no momento de sua criação. Neste caso, todos os cachorros começam com pata = 0 (com a pata abaixada). Vejamos agora o que o Tobby, nosso novo cachorro, faz:

jimmy3

Tobby não é bobo. Não peça pra que ele dê a pata ou a abaixe duas vezes seguidas. E Tobby tem a capacidade de “modificar-se a si mesmo”. Quando levanta ou abaixa a pata, ele altera o atributo pata, que existe dentro de si.

O ponto aqui é que Reference Classes são adequadas para fazer uma aproximação operacional dos conceitos de atributos e ações. Poderíamos fazer isso de inúmeros outros modos, sem apelar para programação orientada a objeto. Mas há simplicidade nos códigos acima. Agentes agem (ou, nos termos de Taylor Swift, “players are gonna play”). Essa representação sugere uma analogia com a realidade. Como diria meu amigo Davoud, especialista em ABM, “it’s all about ontology”.

 

 

 

Anúncios

Git e GitHub: vantagens para sua pesquisa

Um dos maiores problemas das ciências sociais é a questão da replicabilidade. Reproduzir o trabalho de um pesquisador e conferir seus resultados é um dos pilares fundamentais do processo científico, e embora esta seja prática corrente em diversas áreas do conhecimento, seu uso ainda é limitado nas humanidades.  É certo que, em muitos casos, as análises sociais não se prestam facilmente à replicabilidade: pesquisas de campo e descrições de fatos históricos, por exemplo, são por definição baseados em eventos únicos. Mas e quanto aos trabalhos quantitativos? Se a coleta dos dados quantitativos também não é facilmente replicável (sobretudo por custos financeiros), os processos utilizados na análise de dados podem ser analisados e refeitos sem grandes problemas. E quais as vantagens disso?

Três pontos merecem destaque. Em primeiro lugar, ao colocar seus dados abertos para o escrutínio dos pares, o cientista atesta de boa fé que não tem nada a esconder, e que acredita que seus resultados são robustos. Isso dá credibilidade a sua pesquisa, e em termos práticos pode significar o aumento do número de citações do trabalho em questão. Em segundo lugar, é claramente desejável que a ciência seja “autocorretiva”, ou seja, que o conhecimento científico se acumule a partir da revisão, crítica e aprimoramento das pesquisas anteriores. Nesse sentido, abrir os dados e mostrar os procedimentos de um trabalho colabora para que outros possam avaliar e corrigir eventuais erros e formular melhores teorias no futuro. Por fim, há também um importante caráter didático na replicação, cujo valor nem sempre é apreciado. Ao entrar em contato com bancos de dados e ver as ferramentas utilizadas por um autor, um aluno pode entender como procedimentos estatísticos são utilizados na prática, como estimar modelos em uma linguagem de computador e, também, entender que as análises são fruto de muita tentativa e erro. Os gráficos e tabelas que aparecem nas prestigiadas revistas da área não surgiram como mágica, mas são o resultado de diversas modificações até se encontrar a forma adequada para entender o que os dados querem dizer.

Nesse post eu sugiro que os cientistas sociais usem Git [pronuncia-se “guit” ]para essas tarefas. Git é um sistema de controle de versão desenhado por ninguém menos do que Linus Torvals (o criador do Linux), e é usado por empresas como o Google e o Facebook para organizar seus projetos de código aberto. O Git permite que os usuários rastreiem qualquer mudança feita nos dados — sejam eles scripts, bancos, etc — e que eles possam voltar a uma versão prévia desses objetos a qualquer momento. Assim, todas as vezes que algo é salvo no Git, o sistema mostra quais foram as alterações feitas por cada um dos usuários, e caso alguma coisa dê errado, em segundos você pode voltar ao objeto antigo e corrigir os erros. Tudo em um ambiente organizado, sem necessidade de alterar os nomes e os tipos dos arquivos. Bom, não é?

Além disso, o Git também é capaz de criar vários “ramos” (branches) de um mesmo projeto, assim várias pessoas podem trabalhar no mesmo código sem modificar o objeto original. Na minha opinião, não há melhor ferramenta para trabalhos colaborativos, mantendo a transparência de todo o processo.

Um trabalho que não foi feito com Git.

O site mais popular para armazenar repositórios feitos com o Git é o GitHub, mas pode-se criar uma conta também no Bitbucket, no Project Locker e outros. No caso do GitHub, são centenas de projetos de cientistas sociais hospedados lá (por exemplo, o famoso Instituto de Ciências Sociais Quantitativas de Harvard guarda o código de todos os seus programas em um repositório), e você pode segui-los como no Twitter ou Facebook. Assim você também pode interagir e, se quiser, fazer parte dos projetos mais recentes da área.

Segue abaixo um pequeno tutorial para você começar a usar o Git. Primeiro é preciso criar sua conta no GitHub. Caso você só crie repositórios públicos, a conta é totalmente gratuita. Se você quiser guardar códigos privados, os planos começam com 7 dólares por mês, mas há um plano gratuito para estudantes por dois anos.

É possível utilizar as funções mais básicas do GitHub sem instalar os programas que apresento a seguir. Criar um repositório (uma pasta com seus arquivos), colocar seus scripts online (copiando e colando manualmente), e criar uma cópia de um repositório de outra pessoa (fork, no linguajar do Git), tudo pode ser feito direto no site.

Se você tiver uma conta no GitHub, para criar um repositório é só clicar em no “+” no canto superior direito, colocar o nome que desejar (geralmente separados por hífen, como “meu-primeiro-repositorio”), colocar um README no repositório dizendo o que você quiser e clicar em “create”. Pronto!

Criando um repositório no GitHub

Copiar um repositório de outra pessoa para a sua conta também é fácil. Basta ir até o endereço que você quiser e clicar em “Fork” no canto superior direito. Ele estará na sua lista.

Copiando um repositório.

Para adicionar ou modificar arquivos, você precisa fazer um commit. Vamos ver um exemplo. Você pode clicar no README.md do seu primeiro repositório. Para alterar o conteúdo do arquivo, clique no lápis no canto superior direito, edite o arquivo como quiser e coloque uma descrição do que você fez. Isso facilita o entendimento. Depois, é só clicar em “Commit changes” e voilà, ali está o seu arquivo modificado.

Commit, usado para adicionar ou modificar um arquivo

Com isso você já pode usar o GitHub. No entanto, para usar o todo o potencial to Git é necessário instalar o programa. O Git tem versões para Windows, MacOS e Linux, então não há problema quanto à compatibilidade. Para instalar o Git no Linux (aqui uso o Ubuntu como exemplo), você pode usar o apt-get e depois exportar chaves ssh para sua conta. Informe o email cadastrado no GitHub para criar as chaves. Siga as instruções e digite a senha (“passphrase”) que quiser.

apt-get install git
ssh-keygen -t rsa -C "seu@emailcadastrado.com"

E depois disso vá para https://github.com/account, clique em “SSH Public Keys” e em “add another public key”. Copie a chave que você obteve com o comando abaixo e pronto!

notepad ~/.ssh/id_rsa.pub

A equipe do GitHub criou dois softwares simples e fáceis de usar para os outros sistemas operacionais. No MacOS, apenas siga as instruções desta página: https://mac.github.com/. Já para o sistema da Microsoft, é só instalar o Git for Windows, disponível no seguinte endereço: https://windows.github.com/. O processo de instalação é igual ao de outro programa qualquer.

Com o software instalado e uma conta ativa no GitHub você está pronto para usar o Git a partir do seu terminal. Antes de mais nada, é recomendado que você coloque o seu nome e email no Git. Você faz isso com dois comandos:

git config --global user.name "Seu Nome"
git config --global user.email "seu@email.com"

Agora é só começar a trabalhar nos seus projetos. Para copiar um repositório do GitHub para o seu computador, digite:

git clone https://github.com/seunome/seurepositorio.git

Caso você queira criar um novo repositório a partir de seu computador, vá até a pasta que você quer utilizar e digite:

git init

Isso vai indicar ao Git que aquela pasta deve ser incluída.

Agora vamos adicionar um arquivo a um repositório do GitHub. Neste exemplo, vamos supor que a pasta contém um arquivo chamado “teste.R”, um script para o software estatístico R.

git add teste.R
git commit -m "um script para R"
git push origin master

A sequência é sempre “add”, “commit” e “push”. “Add” adiciona o arquivo (“git add -A” adiciona todos os arquivos da pasta), “commit” salva o estado atual do seu repositório e “push origin master” envia o arquivo para o branch “master”, o padrão. Se você chegou até aqui, já pode usar o Git sem problemas.

Caso você tenha modificado algum arquivo direto no site do GitHub, você também pode sincronizar a pasta do site com os arquivos locais do seu computador. É só digitar esse comando:

git pull

Sua pasta local deve estar atualizada agora. Por fim, caso tenha algum comando que você não sabe como funciona e quer ler mais sobre ele, é só digitar “help”. Por exemplo, para saber mais sobre “pull”, digite:

git help pull

Essas são as operações básicas do Git. Com elas você pode escrever, alterar e publicar seus scripts e dados. Mas isso é só o começo. Para uma introdução mais detalhada ao Git, você pode consultar o excelente livro online Pro Git (em português), ou os guias do GitRef.org e do próprio GitHub (em inglês). Se você tiver qualquer dúvida ou sugestão, deixe um comentário abaixo. Até mais!

Agent-Based Modeling para ciências sociais: simulando o dilema do prisioneiro

Agent-based model ( ABM ) é um tipo de modelo computacional que simula agentes que interagem para explicar resultados de larga escala que decorrem do sistema como um todo. Ok, você não entendeu nada, correto? Mas saiba que é um paradigma contemporâneo importante das ciências sociais e está sendo cada vez mais utilizado com resultados muito positivos. O objetivo do post de hoje é tentar explicar alguns conceitos que fundamentam essa abordagem e oferecer um teste prático, simulando o célebre “dilema dos prisioneiros”, tema famoso da teoria dos jogos.

As ciências em geral, incluindo as sociais, utilizam uma estratégia de pesquisa focada em modelos. Modelos nada mais são que simplificações da realidade. Pense num mapa, por exemplo, do Estado de São Paulo.

Esse mapa permite que você obtenha informações importantes da realidade (como chegar do lugar A ao B), deixando de lado uma série de detalhes desnecessários.

Um modelo científico é algo semelhante. Ele pretende oferecer simplificações que expliquem bem a realidade, centrando-se em determinados pressupostos. Por exemplo, imagine modelos que expliquem o comportamento judicial de juízes de Suprema Corte. Alguns modelos consideram apenas fatores ideológicos, outros, apenas fatores estratégicos. O que eles fazem é oferecer explicações das decisões com base em uma ou outra causa (mas é claro que você pode incluir mais de uma).

Os modelos científicos podem ser de vários tipos: lineares, não-lineares, estocásticos, determinísticos, entre outros. Aqui nós trataremos do modelo baseado em agentes (ABM), adequados para a análise de sistemas complexos adaptativos. Esse tipo geralmente é contraposto a um modelo reducionista Newtoniano, focado em cálculo diferencial, experimentos controlados, etc. O ABM concebe a realidade social como complexa e trata de simulações computacionais das ações (e interações) de agentes (que podem ser indivíduos, grupos, organizações) como forma de obter informações emergentes do sistema como um todo.  

Imagine que você pretende modelar várias formigas e entender como essas formigas, através da ação individual de cada uma, conseguem formar uma ponte.

Modelar esse comportamento envolve entender como a ação individual de cada uma (em um nível micro) pode derivar na formação da ponte (nível macro).  Não só de biologia estamos falando, poderíamos também tentar entender, na ciência política, como da ação, por exemplo, de juízes ou parlamentares poderiam emergir propriedades macro. Pense em Adam Smith: como da ação de indivíduos movidos por auto-interesse poderia emergir o crescimento econômico? (Caberia aqui falar mais de “sistemas complexos adaptativos” e “emergência”, mas, tendo em vista  a brevidade do post, deixarei essa discussão para posts posteriores).

Para modelar esse tipo de situação é interessante realizar simulações computacionais de agentes. Um dos pioneiros no uso dessas simulações em ciências sociais foi o cientista político Robert Axelrod, escritor de “The Evolution of Cooperation”.

Ele queria entender como a cooperação poderia emergir e persistir da ação de seus indivíduos. Para isso, ele partiu do exemplo clássico da teoria dos jogos, o “Dilema do Prisioneiro”.

“Temos dois suspeitos, Jay e Bill, que são presos pela polícia. Cada um deles é mantido isolado do outro, sem qualquer tipo de comunicação. A polícia sabe que não tem evidências suficientes para indiciar os dois no crime principal, o que acabaria restando para cada um deles 1 ano de prisão. A polícia então oferece um acordo: se um deles confessar, mas o outro não, o que confessou (não-cooperação) sairia livre e o que não confessou cumpriria 10 anos de prisão; se os dois confessarem, cada um cumpriria 5 anos.

A matriz de payoffs é a seguinte:

É amplamente conhecido que o equilíbrio de Nash para esse jogo é cada um trair. É fácil ver que, se um dos suspeitos cooperar ou não-cooperar, o outro estará numa situação bem melhor se sempre não-cooperar. Mas o que ocorreria se você repetisse esse jogo várias vezes com vários jogadores?

O que Robert Axelrod fez foi criar um torneio em que teóricos enviariam suas estratégias (em formas de algoritmos) e ele testaria uma contra a outra, em rounds diferentes. A estratégia que foi vencedora, conhecida como “TIT FOR TAT”, foi enviada por Anatol Rapoport e seu código continha poucas linhas. Ela consistia no seguinte: 1) começar cooperando; 2) repetir a jogada anterior do outro jogador. Isto é, se o outro jogador trai numa rodada, na seguinte, no TIT FOR TAT, você também trai; se cooperar, na seguinte você também coopera.

Bom, vamos às simulações. O que ocorreria se você colocasse vários jogadores com estratégias diferentes jogando simultaneamente o dilema do prisioneiro com estratégias distintas?

Um software bastante utilizado para esse tipo de simulação é o NetLogo.

É um ambiente com linguagem de programação para a criação de modelos baseados em agentes. Foi criada por Uri Wilensky, da Northwestern University, baseada na linguagem Logo (que era bastante utilizada antigamente para ensinar crianças a programar). Pode ser baixado no site http://ccl.northwestern.edu/netlogo/ .

Ele é usado para simular vários tipos de situações, desde teorias contratualistas (Hobbes e Locke) até modelos de transporte urbano.

A ideia aqui é simular o Dilema do Prisioneiro. O NetLogo tem uma boa biblioteca de modelos prontos para teste (você pode tentar brincar com algum deles). Para isso, abra o Netlogo e vá em “File >> Models Library”. Na tela de “Models Library”, vá em “Sample Models >> Social Science >> (unverified) >> PD N-Person Iterated”. Para quem não quiser fazer o download, deixei um applet neste site https://linux.ime.usp.br/~jardim/dilema, que executa a simulação no próprio navegador.

modelslibrary

Aberto o modelo, o próximo passo é ajustar quantos jogadores você vai colocar. A estratégia “random” corresponde a trair ou cooperar de maneira aleatória. “Cooperate”, a sempre cooperar. “Defect”, a sempre trair. “Tit for tat”, ao TIT FOR TAT já explicado. “Unforgiving” , a uma estratégia em que você vai sempre cooperar  até ser traído; a partir daí, passa a trair sempre. “Unknown” é uma estratégia para ser programada posteriormente, então não vamos colocar nenhum jogador para essa.

Vamos colocar 10 jogadores para cada estratégia, exceto para unknown, que vamos deixar 0. O próximo passo é clicar em “Setup”. Você obterá uma tela parecida com esta.

prisoners

A partir disso, clique em “Go”. A simulação vai começar. Verifique os resultados em “Average Payoff”. 

Veja que, após alguns “ticks” (a medida de tempo), parece que a estratégia de sempre trair é a que rende um maior valor para seus jogadores.

defectap

Porém, após 10000 ticks, repare que as estratégias de “unforgiven” e “tit for tat” parecem ser as grandes vencedoras. A situação mudou completamente!

unforgiven

 

O NetLogo permite que você teste os modelos já prontos e que crie os próprios. Em posts próximos, pretendo tratar mais dos conceitos que fundamentam o ABM, como o de “sistemas complexos adaptativos” e “emergência”. Também pretendo fornecer mais dicas para quem deseja programar no NetLogo e listar um material disponível sobre simulações.

 

DataFrame e análise estatística em Python: usando o pandas

pandas_logo

Este post é resultado das minhas tentativas de utilizar o Python como uma alternativa ao R. Não quero entrar em discussões sobre qual linguagem está se sobressaindo (como no texto  “The Homogenization of scientific computing or Why Python is steadily eating other languages lunch”  ) ou então qual tem um desempenho melhor. A verdade é que eu estou me sentindo cada vez mais confortável com o Python e estou ficando bastante surpreso com as possibilidades dessa linguagem para webscraping e estatística.

Tendo em vista isso, quero mostrar de maneira básica como trabalhar com Dataframes na linguagem e também como realizar uma regressão OLS simples.

Para instalar o Python 3, faça download em http://python.org/download/releases/3.0/ . Abra o interpretador, você provavelmente terá uma tela semelhante a esta:
python3

Vamos ao código. Para o teste, utilizei o banco de dados de Baum and Lake (2001), para o artigo “The Invisible Hand of Democracy: Political Control and the Provision of Public Services” . O banco de dados, em formato .xls, está disponível no seguinte link: (  http://dss.ucsd.edu/~dlake/Data/invisible_hand_data2.xls/  ).

Utilizei o módulo pandas, que é uma biblioteca para estrutura de dados e análise para python.  Para instalar o pandas, faça download em: http://pandas.pydata.org/.

Como alternativa mais fácil, você ainda pode instalar o pyzo, que é uma distribuição python, com uma série de pacotes, incluindo pandas, NumPy, SciPy e um IDE ( http://www.pyzo.org/downloads.html ). Na verdade, instalar o pyzo é até mais viável, uma vez que você não terá de resolver as dependências na mão.

Vamos ao código. Aberto o interpretador, o primeiro passo é importar o pandas:

 import pandas
 

Em seguida, vamos importar os dados da primeira planilha do arquivo para um dataframe.

df=pandas.read_excel('/home/user/Downloads/invisible_hand_data2.xls', 'Cross-Sections')
# Especificar o caminho do arquivo na primeira parte

Como podemos ver, definiu-se um DataFrame ‘df’, contendo os dados da planilha. Para verificar alguns dos valores, tente o seguinte:

print(df.values)
print(df.columns)

Seu output será algo semelhante a isto:

python4

Para descrição dos dados, podemos utilizar o método ‘describe’ para o objeto ‘df’.  Por exemplo, para obter a média, o desvio-padrão e outras estatísticas para a coluna ‘gnp’, podemos tentar:

df['gnp'].describe()

Por fim, vamos realizar uma regressão OLS simples, para o ano de 1990, utilizando um dos modelos do artigo de Lake and Baum:
illitpop = ß0 + ß1 dem + ß2 gnp + ß3 landarea + ß4 urban population + ß5 population + ß6 OECD + u

Para isso, vamos selecionar os dados apenas para 1990 e, em seguida, rodar a regressão.


dfols=df[df['year']==1990] # Apenas para 1990

ols=pandas.ols(y=dfols['illitpop'], x=dfols[['gnp', 'landarea', 'urbanpop', 'population', 'oecd', 'dem']])

Um simples comando ‘ print(ols) ‘ vai nos mostrar os resultados:

ols

Bom, é isso. A verdade é que o pandas conta com muitos outros recursos, os quais mostrarei em posts posteriores. Espero que este seja útil para aqueles que pretendem experimentar um pouco com o Python (sobretudo para aqueles que estão acostumados com o R). Até a próxima.

Investimento Estrangeiro Direto no Brasil (mapa por País de Origem Imediata)

Um uso muito legal de coisas sobre as quais já conversamos aqui no Sociais & Métodos.
PS.: Conheçam o Análise Real, Blog de Carlos Cinelli

Análise Real

Que tal visualizar os dados do Censo de Capitais Estrangeiros de uma maneira diferente?

Abaixo, mapa com a distribuição do Investimento Estrangeiro Direto (IED) no Brasil, critério participação no capital, em 2010, segundo o país de origem imediata. O mapa foi feito no R. Quanto mais escuro, maior o investimento daquele país em empresas brasileiras.
IED_Pais

PS: agradeço ao Rogério pelo didático post ensinando o caminho das pedras.

Ver o post original

Milhões de casos em segundos: os Censos no R

funilCensos Demográficos são bancos de dados muito pesados! Milhões de casos… Operações simples, como frequências, médias e proporções podem demorar muitos minutos. Modelos estatísticos complexos podem demorar horas… ou dias.  O  R convencional (assim como o Stata) carrega todos as informações com as quais está trabalhando na memória RAM. Ou seja, as análises são realizadas muito rapidamente, mas não é possível abrir um banco que seja maior do que a memória disponível. O SPSS e o SAS executam as análises a partir dos arquivos no HD – assim, suportam bancos grandes, mas são muito lentos.

Neste em 2012 e 2013 trabalhei muito com os Censos brasileiros (cf. Projeto Censo). Era necessário descobrir uma maneira de agilizar as análises. Foi quando descobri uma versão do feita exatamente para lidar com grandes bancos de dados, produzida pela Revolution AnalyticsEssa  empresa construiu um software (não aberto) em cima do R convencional — o RevolutionR. Ele funciona com a mesma linguagem, suporta os mesmos pacotes e funcionalidades — mas com alguns adicionais (como dizem, “100% R and more“). Uma versão gratuita do RevolutionR para uso individual ou acadêmico pode ser baixada aqui. Uma das principais diferenças é a presença de um pacote para “big data”, que não pode ser instalado no R convencional, chamado RevoScaleR. Quais são suas vantagens?

  • É possível trabalhar com dados de qualquer tamanho, pois ele acessa informações a partir do HD (assim como o SPSS e o SAS), superando os limites de memória RAM.
  • O RevolutionR permite salvar dados num formato próprio, com extensão XDF. Nesses arquivos, grandes bancos de dados são fragmentados e salvos em blocos separados (como uma planilha com múltiplas abas), chamados chunks ou blocks. Processar cada bloco de uma vez é menos pesado do que trabalhar com o banco todo (esse é o princípio já adotado, no R, por pacotes como ff, ffbase e biglm).
  • As funções de análise já acessam e integram os resultados dos múltiplos blocks. E tudo isso já fazendo uso de processamento paralelo — usando todos os cores de um computador ou vários computadores ao mesmo tempo.

Resumo: uma regressão múltipla com 25 milhões de casos pode ser feita em menos de 10 segundos.

Vamos por a mão na massa então. Vou usar os dados do Censo de 2010, que podem ser baixados no site do Centro de Estudos da Metrópole, como já dissemos aqui (lembrem-se dessas instruções, antes de usar os microdados).

Em primeiro lugar, é necessário dizer que vamos usar computação paralela, todos os cores do computador:

# Define um contexto de computação paralela
parallelContext = RxLocalParallel()
rxSetComputeContext(parallelContext)

Depois temos que salvar o arquivo .SAV (formato SPSS) como XDF (formato RevolutionR). Essa é a única parte demorada: 40 minutos ou mais… Mas só precisa ser feita uma vez.

#Indicando o local do arquivo
censo2010spss = RxSpssData(file.path("C:/dados/censo2010.sav"))

#"Salvando como"
rxImportToXdf(
	inSource = censo2010spss,
	outSource = "C:/dados/censo2010.xdf",
	reportProgress = 1,overwrite = TRUE)

Então apontamos o local do arquivo XDF no HD:

censo2010 = file.path("C:/dados/censo2010.xdf")

Agora é moleza. Que tal um gráfico da média de renda por idade?

# Tabela: Média de renda por idade -- 10 a 80 anos  (com peso)
censoCube = rxCube(renda.trab ~ F(idade.anos), data=censo2010, 
   rowSelection = (idade.anos >= 10) & (idade.anos <= 80), 
   fweights = "peso_pessoa") 

# Tranformando a idade de factor para numeric
censoCube$idade.anos = as.integer(levels(censoCube$F_idade.anos))

# Transformando a tabela num data.frame
censoCube = as.data.frame(censoCube) 

# Pronto: gráfico de renda por idade
rxLinePlot(renda.trab ~ idade.anos, data=censoCube,
   title="Relação entre Renda e Idade",
   xlim=c(0,85), ylim=c(0,2000))

O que acham?

grafico

Bonitinho, né!? E o mais interessante: Computation time: 3.697 seconds. Mais? Vamos repetir o mesmo gráfico, mas com recorte por sexo.

# Uma regressão para estimar as médias de renda
# por idade e sexo (efeito interativo). A opção
# "cube=TRUE" indica que salvaremos o cruzamento
linMod = rxLinMod(renda.trab~sexo:F(idade.anos), data=censo2010,
   pweights="peso_pessoa",  cube=TRUE, 
   rowSelection = (idade.anos >= 16) & (idade.anos <= 80)) 

# Extraio a tabela de cruzamento
plotData = linMod$countDF

# Converto a idade de factor para numeric
plotData$idade.anos = as.integer(levels(plotData$F.idade.anos.))[plotData$F.idade.anos.]

# Gráfico de idade e renda por sexo
rxLinePlot(renda.trab~idade.anos, groups=sexo, data=plotData,
   xlim=c(0,85), ylim=c(0,2500))

Computation time: 4.053 seconds Nada mal, né!? Uma regressão, transformações em variáveis e um gráfico…

grafico2

Pois é… seu laptop era um super computador e você nem sabia…

Nota:  As flutuações uma flutuações na renda do trabalho nas idades mais avançadas provavelmente são devidas ao baixo número de casos de trabalhadores nessas faixas. Logo, outliers passam a influenciar mais as observações. Além disso, podem ter ocorrido problemas na captação da informação sobre renda e há limites intrínsecos à própria amostra (os microdados do Censo abarcam +/- 10% da população, cerca de 21 milhões de casos). Mas esta não é uma particularidade do Censo de 2010. Em outros censos pesquisas (como PNADs, PMEs, PEDs etc) encontramos o mesmo.

Enxergando o boom da pesquisa empírica em direito no Brasil

O post de hoje utiliza automação de coleta despretensiosamente para verificar como anda a pesquisa em Direito no Brasil.

Estava brincando com um código em R para coleta de dados no Google Scholar, quando me surgiu a ideia. Embora o blog seja de sociais em geral, a minha área de pesquisa é em Direito,  e eu pensei em tentar levantar quantas publicações desde a década de 90 tinham um enfoque empírico.

Nos últimos anos, pesquisadores nessa área passaram  a se preocupar mais com metodologia científica (preocupação quase inexistente anteriormente). Surgiram trabalhos de enfoque “empírico”, contrapostos ao que se produzia antes, pejorativamente chamados de ‘dogmáticos’. Até como um sinal de ‘status’, passou-se a acrescentar nos subtítulos dos trabalhos as expressões “viés empírico”, “estudo empírico”, “pesquisa empírica”.  São artigos qualificados assim que eu tentei levantar. O que eu esperava encontrar era um crescimento forte do número de publicações, sobretudo nos últimos anos.

No geral, o que eu fiz foi realizar consultar rápidas no Google Scholar com os termos de busca “intitle:empírico direito”. O Google vai retornar todos resultados que contenham no título a palavra “empírico” e no corpo do texto, a palavra “direito”. Também consultei “intitle: empírica direito” e juntei os dois resultados.  Em seguida, criei um gráfico para mostrar a frequência pelo tempo. 

artigos_anos1

Aí está. Para publicações com a palavra “direito” no texto e “empírico/a” no título, temos um aumento pequeno por volta de 2001 e 2002, um aumento muito grande entre 2005 e 2007 e uma tendência de aumento para os anos seguintes. O encontrado é consistente com o esperado.

Porém, há viés na coleta. Nem todos os artigos ‘empíricos’ vão conter a expressão no título (entretanto, os pesquisadores insistem em fazer isso). Também nem todos os artigos que contenham “direito” no corpo do texto serão da área jurídica. Ainda o número de observações é pequeno (n=487).

Adotei, assim, uma outra estratégia. Usei o crawler para buscar no Google Scholar a expressão ‘intext:”pesquisa empírica”  com 6 expressões diferentes: “direito constitucional”, “direito administrativo”,   “direito tributário”, “direito ambiental”, “direito penal”, “direito civil”, isto é, agora a busca está sendo feito apenas dentro do texto (e não no título). Os gráficos seguintes resumem os dados coletados:

Todos os gráficos mostram um aumento pelos anos. Entretanto, diferentemente do gráfico anterior, o aumento mostrado agora é muito maior nos últimos anos, entre 2009 e 2012, sobretudo no último (2012). Por fim, ainda plotei as frequências correspondentes a todas essas áreas juntas (n=1578) (deixando de contar propriamente os dados repetidos para cada uma das variáveis):

artigo_3

O aumento maior para os dados juntos são, assim, entre 2009 e 2012.

No entanto, essa estratégia distinta também não escapa do viés, sobretudo porque estamos selecionando os dados por áreas (constitucional, administrativo, civil, etc).  Como eu disse no primeiro parágrafo, esse post é só um teste despretensioso de um algoritmo, não pretende fazer nenhum tipo de inferência mais robusta (o que exigiria uma coleta mais atenta).

De qualquer forma, com a tentativa, dá para enxergar que a pesquisa empírica em direito, no Brasil, teve três períodos de expansão. Um, por volta de 2001; outro, por volta de 2006; e um último, entre 2011 e 2012. Dá para pensar numa série de explicações possíveis para isso, como as novas reflexões sobre o direito no primeiro momento (por exemplo, o artigo de Marcos Nobre, “Apontamentos sobre a pesquisa em direito no Brasil”,2004) e a iniciativa de grupos dedicados à metodologia e pesquisa empírica nos últimos anos (como a Rede de Pesquisa Empírica em Direito – REED <http://reedpesquisa.org>) . Tentativas de explicações melhores, entretanto, ficam para um próximo post.

Atenção: As consultas realizadas não indicam apenas artigos nacionais, mas todos de língua portuguesa. Entretanto, consultando visualmente a base de dados, dá para perceber que a grande maioria é de fato do Brasil.

Aluguéis em SP II: a vez do Zap Imóveis

Um balanço e um mea culpa

Fiquei bastante contente com a recepção ao post anterior sobre aluguéis em SP. Foram muitos os comentários aqui no WordPress, no Facebook, no Twitter e também pessoalmente. E foram várias as correções às minhas afirmações. Agradeço bastante pelas críticas e dicas para melhorar a abordagem! Listo alguns pontos importantes que me foram ditos:

  • São raras as verdadeiras “imobiliárias online”. O que investiguei são sites de anúncio e não operadoras de contrato ou administradoras de imóveis. Assim, o modo como me referi a esses sites estava incorreto.
  • Anunciar nos sites centrais é realmente custoso ($$$). Isso corrobora minha suspeita de que esta é a fonte do viés de cobertura geográfica: as maiores imobiliárias pagam pelos anúncios mais caros e anunciam imóveis caros em regiões ricas.
  • O preço do anúncio é diferente do preço de negociação e de fechamento do contrato. É esperado que, de modo geral, em todos os lugares, os preços da internet sejam maiores do que a média dos aluguéis efetivamente cobrados.

Fiquei muito interessado em comparar o preço anunciado pelos sites com os preços cobrados nos contratos pelos mesmos imóveis. Mas desconheço fonte de dados para isso. Também gostaria de de coletar dados de sites de anuncio menores, mais baratos ou gratuitos. Mas não tive tempo nem fôlego. Tá anotado na “to do list”.

Friso que sou leigo tanto nas técnicas que utilizei (análise espacial) quanto dos temas em que meti meu bedelho (habitação, política habitacional, mercado imobiliário etc.) De início, queria apenas testar um algoritmo de webscrapping! Mas gostei do resultado e quis postar. A propagação do post e a velocidade de resposta me mostraram que essa prática de academic blogging pode ser, em certos casos, bem mais eficiente do que a escrita e a publicação acadêmica stricto senso (a que estou acostumado). Houve muito mais debate do que usualmente vejo por aí…

O Zap Imóveis

Sem tempo pra bolar algo muito diferente, acabei fazendo um repeteco do formato de análise anterior. Mas agora com o ZapImóveis — que creio ser o maior site do gênero.

Fiz a pesquisa no dia 11/10/2013. Busquei por apartamentos residenciais para alugar, com informação sobre endereços. Retornaram 646 páginas de resultados. Eliminando casos repetidos e com informação incompleta, fiquei com 12.794 entradas — coisa pra caramba! E fiz a mesma coisa da outra vez: georreferenciei (obtive latitude e longitude) o ponto médio de cada bairro onde os imóveis estavam localizados e plotei. O código em R para a replicação completa está neste link.

Vamos a alguns resultados. O mapa abaixo mostra o preço mediano do aluguel por metro quadrado em cada um dos bairros cobertos pelo ZapImóveis.

plot_zoom

Uma outra forma de ver os mesmos resultados é através de linhas de densidade, que tendem ao violeta quando os preços são mais caros:

plot_zoom2

Algumas informações extraídas daí são velhas conhecidas: os aluguéis caros estão nos bairros ricos…

As cores do primeiro mapa aparentemente sugerem que há preços mais baratos no ZapImóveis do que no Imóveis Web. Mas isso não é bem verdade, como mostra o gráfico de barras abaixo. A maioria dos valores se concentram também na faixa dos 25-45 reais:

histograma

Com relação à densidade da cobertura geográfica, há enormes similaridades com o padrão verificado no ImóvelWeb. As regiões em que há alta oferta de alugueis são praticamente as mesmas, a despeito do fato de que foi colhida uma quantidade quase dez vezes maior de casos. Isso não nega o fato de que esse número maior de casos representou inclusão de bairros e áreas mais variados, de modo geral. No entanto, os padrões de concentração ou viés de cobertura são os mesmos. Provavelmente, pelas mesmas razões: os elevados custos para anunciar são seletivos, quase proibitivos para corretores individuais e pequenas imobiliárias.

densidade

Segundo os dados que coletei, a média do preço para a cidade como um todo é de R$ 46.42/m². E a mediana é de R$ 35.50/m². Esses valores certamente são inflados pelo viés de concentração mostrado acima… ou seja, a excessiva quantidade de imóveis noticiados em Moema, Brooklin e região faz com que a média global seja superestimada. 

No entanto, esses valores estão também muito acima dos preços dos contratos de aluguel dessas mesmas regiões: segundo o Sindicato da Habitação (Secovi), em agosto de 2013, o preço médio metro quadrado em imóveis para alugar na zona Centro-Sul variava entre R$ 26,48 e R$ 33,61 (vejam esse link).

Façamos umas contas. Dividindo a média dos alugueis anunciados no ZapImóveis pelo menor

R$ 46.42/m² /  R$ 26,48/m²   = 1,75 

O que isso quer dizer? Que o preço médio do metro quadrado nos anúncios pode ser até 75% maior do que o preço médio dos contratos.

Tá… suponhamos que os preços notificados sejam negociáveis, quando o cliente bate à porta da imobiliária e visita pessoalmente os apartamentos. Mas serão comuns descontos de 50% ou mais? Não creio.

Sublinho que não estou fazendo um estudo rigoroso sobre o assunto. Mas esses resultados tentativos me sugerem que os anúncios online dos grandes sites não oferecem vantagens competitivas para o consumidor. A redução dos custos informacionais parece não vir sempre acompanhada da redução dos custos financeiros…

Para inferências mais precisas, seria necessário mais cruzamentos e análises detalhadas das regiões, o que não tenho como fazer no escopo deste post apressado. Ainda quero fazer pesquisa nos sites menores e gratuitos, como mencionei acima.

A discussão continua. Já adianto meus pedidos de desculpas por quaisquer erros e simplificações. Aceito comentários e sugestões! A extração de dados da internet abre realmente oportunidades muito interessantes, certo!?

Aluguéis em SP: o viés das imobiliárias on-line

Procurar um lugar pra morar é bastante estressante — principalmente tendo em vista que tempo e dinheiro são bens escassos. Contudo, aparentemente, essa tarefa se tornou mais fácil, com o advento dos sites que conjugam num só lugar diversas imobiliárias. Pretensamente, esses sites também acirrariam a competição, por diminuírem o custo de obtenção da informação, “empoderando” o potencial cliente. Ou seja, com mais competição, se espera menor preço. Mas recentemente descobri que isso pode não ser bem verdade…

Li o post São Paulo, uma cidade gentrificada, do Arquitetura da Gentrificação e fiquei pensando se eu conseguiria fazer um levantamento de preços de aluguéis a partir desses sites de imóveis e então calcular a média do que se costuma cobrar por metro quadrado. Construí dois scripts de webscrapping em R: um para fazer download das informações do ImóvelWeb e outro para o ZapImóveis. Mas neste post, vou explorar apenas as informações do ImóvelWeb.

Baixei informações sobre apartamentos para locação (excluindo casas, lofts, studios, kitnets e imóveis comerciais). Montei um banco de dados com informações sobre preço, área (metros quadrados), bairro, nº de banheiros, quartos, suítes e vagas em garagem. Sei que esses sites tem entradas repetidas de imóveis (às vezes devido ao fato de estarem simultaneamente no cadastro de duas ou mais imobiliárias). Exclui os duplicados — certamente jogando alguns fora indevidamente e ainda deixando passar outros (que apesar de aparecerem mais de uma vez, não tem registros exatamente idênticos).

Depois obtive a latitude e longitude dos pontos médios de cada bairro onde estavam localizados com uso da função geocode (sobre a qual já tratei em post anterior). Sim… seria mais legal se fosse possível geocodificar os endereços exatos dos apartamentos, mas não há essa informação para todos os casos. No final das contas fiquei com dados para 1426 apartamentos.

Baixei um mapa do GoogleMaps no R e plotei a mediana do valor por metro quadrado para cada bairro. O resultado é a figura abaixo:

precos

Algumas observações

  1. De forma nada surpreendente (para alguém que vive em São Paulo), os lugares mais caros são: Itaim, Moema, Brooklin, Jardins, Morumbi… Mas esses dados são um pouco diferentes daqueles do Arquitetura da Gentrificação: Pinheiros, Perdizes e Pompéia são sim caros… mas não estão no mesmo páreo que aqueles.
  2. Reparem nos preços: grande parte das bolinhas azuis são indicadoras de valores entre 25 e 45 R$/m². Isso parece bem acima dos valores médios identificados por eles.

Obviamente há diferenças importantes na composição da amostra e nas fontes de dados. Não são dados completamente comparáveis. Creio que as duas pesquisas, a minha e a deles, dizem respeito a coisas diferentes. Assumo que os dados deles informam “de fato” sobre a tendência dos aluguéis em SP… Os meus informam sobre o características do ImóvelWeb — que podem ser avaliadas tomando os dados deles como parâmetros.

Plotei também uma mancha de densidade dos imóveis para locação, para ter uma noção da cobertura e dos lugares mais anunciados. No mapa abaixo, quanto mais azul claro, maior a frequência de imóveis anunciados na região:

cobertura

As conclusões (tentativas) são simples:

  1. Os valores de aluguel informados pelas imobiliárias online estão acima da média da cidade para quase todos os bairros (as bolinhas, afinal, indicam preços comparativamente mais altos por toda extensão do mapa). Alugar pela internet parece ser mais caro!
  2. O ImóvelWeb (e possivelmente outros sites desse tipo — ainda tenho que verificar) possui um viés de cobertura em favor dos bairros ricos. O número de imóveis notificados em determinadas localidades é mínimo. Não se reduz o custo informacional igualmente para todos os lugares.

Arrisco a razão para esses fatos: provavelmente apenas as maiores imobiliárias anunciam nesses sites. Assim, despontam na frente das pequenas, em termos de visibilidade, e podem cobrar um preço maior. A competição talvez ocorra sim, mas apenas entre essas grandes. Suspeito que talvez encontraríamos preços mais baratos batendo na porta de alguns escritórios, à moda antiga mesmo. Se a razão que apontei está correta, fica também fácil compreender o viés de cobertura: as maiores imobiliárias são aquelas que atendem os ricos.

O script para a replicação completa está disponível neste link. Não está muito bonitinho, nem muito formatado. É a pressa.

Stata – Prós e Contras

Panorama Geral

Criado em 1985, Stata (atualmente em sua 13º versão) é um software estatístico para análise e manipulação de dados. Utilizado, principalmente, nas áreas de Economia, Sociologia e Ciência Política, o Stata não é vendido em módulos (diferentemente do SPSS ou SAS). Ou seja, ao comprar o Stata o usuário recebe o software completo. Todavia, o usuário não é limitado ao pacote de comandos inicialmente oferecido pela empresa; uma vez que com o Stata conta com uma comunidade ativa de usuários que produz complementos ao software (os arquivos “.ado”). Estes pacotes possibilitam aos usuários expandir o escopo de funções disponíveis e previamente existentes.

Um software “user friendly”

Três são as características que fazem do Stata um dos softwares de análise mais amigáveis ao usuário: sua interface gráfica, sua linguagem de comandos (sintaxe) e seu manual.

Tal qual no SPSS, no Stata o usuário tem duas opções para manuseio dos dados: utilizar a interface gráfica ou escrever os comandos nos do-files. A opção de utilizar os do-files possibilita, como em outros softwares estatísticos, controlar e recuperar o que foi realizado na análise. Por outro lado, a interface gráfica (isto é, o conjunto de menus e botões que executam comandos sem necessidade de escrever códigos) é útil para os usuários que estão aprendendo o software e, em alguns casos, até para usuários avançados. Todos os comandos executados por meio dos menus aparecem posteriormente como códigos na janela de resultados. Ou seja, a interface gráfica pode ser utilizada para o aprendizado das sintaxes de comandos mais avançados. O que é extremamente útil para a construção de gráficos.

Nas imagens abaixo construímos um histograma com a curva normal sobreposta para exemplificar o uso da interface gráfica.Utilizaremos os dados do Censo americano de 1980 (exemplo disponibilizado pela instalação do Stata). Para abrir os dados, basta escrever na janela de comandos a seguinte expressão:

sysuse census.dta

Depois podemos ir até o menu “Graphics” conforme a figura abaixo:

1

No menu, selecionamos a opção “Histogram”. Na caixa de diálogo, escolhemos a variável de interesse. Analisaremos a população de cada um dos estados americanos em 1980.

2

Na caixa de opções do gráfico, há diversas opções de formatação. Podemos, por exemplo, informar ao Stata se nossas variáveis possuem peso (aba “Weight”); ou definir opções visuais, como o esquema de cores, legendas e títulos. Optamos apenas por adicionar uma curva normal. Na aba “Density plots”, marcamos “Add normal-density plot”.

3

Temos o resultado, ou seja, o nosso gráfico. É possível observar que a sintaxe necessária para replicar o gráfico apareceu na janela de resultados.

4

No formato padrão das sintaxes do Stata, a primeira palavra é o comando seguido pela variável de interesse (e quando necessário, pelas variáveis independentes). Expressões depois de uma vírgula são definições opcionais. Se retirarmos a opção “normal” teríamos o histograma sem a curva normal. Porém, o usuário pode, em diversos momentos, escrever apenas uma abreviação do comando:

hist pop, norm

Obteríamos o mesmo resultado. A regra para que o Stata entenda comandos curtos é simples: não poderíamos escrever “his” para obter um histograma, pois existe outro comando com esse mesmo início.

Por fim, cabe apontar o terceiro pilar que torna o Stata um software amigável: o manual. O manual do Stata é uma das fontes mais completas de informação que um usuário pode ter do Software. Para acessa-lo, basta clicar no menu “Help” e depois em “PDF Documentation”. Há explicações para todos os comandos internos do Stata. Além disso, há referências bibliográficas e interpretações para resultados. Com isso, o manual do Stata serve ao mesmo tempo para o aprendizado da linguagem como para a apresentação de conteúdos estatísticos substantivos.

O lado ruim…

Mas Stata também apresenta seus lados negativos…

Apesar da existência de uma quantidade considerável de expansões (pacotes desenvolvidos por usuários), o número de pacotes disponíveis é menor do que os disponíveis para o R. Ou seja, há mais funcionalidades disponíveis no R  do que no Stata. Um exemplo é a ausência da função de web crawler.

Mas os pacotes do Stata possuem uma vantagem: são submetidos à avaliação de programadores e estatísticos para garantir completa funcionalidade. Os pacotes avaliados recebem um certificado da empresa e seus autores ganham uma publicação no Stata Journal.

O segundo ponto negativo é o preço. Esse problema é agravado para nós brasileiros, pois poucas Universidades possuem convênio com o Stata para baratear o valor do software para seus alunos. Além disso, as versões que possibilitam processamento paralelo podem chegar a custar mais de R$1000,00. Não há mágica para resolver esse problema, porém a boa notícia é alguns programas de pós-graduação têm começado a investir em softwares.

Opções no mercado

Atualmente, o Stata oferece cinco diferentes versões para estudantes.

A versão Small é a que possui mais limitações. Ela indicada apenas para usuários com bancos de dados muito pequenos. A versão IC comporta o tratamento da maior parte dos bancos de dados utilizados em disciplinas, como Economia Política. Em geral, com o Stata IC o usuário consegue trabalhar bem com dados para países e de surveys. Já as versões SE e de processamento múltiplo (MP) diferem quanto à velocidade de processamento.

É notável o ganho de velocidade no tratamento de dados com o uso de versões de multi-processamento. A maioria dos usuários não sente tanta diferença, no entanto, uma vez que ainda são poucas as pesquisas em Ciências Humanas com “Big Data”. Mas se o usuário for utilizar bancos de dados com mais de 500 mil casos ou mesmo Censos, recomendamos que seja utilizado um software com processamento paralelo.

Versão

Número Máximo de Variáveis

Número máximo de Variáveis Independentes

Número Máximo de Observações

Proporciona processamento paralelo?

Plataformas

Preço para estudantes (Versão Perpétua)

Stata/MP2

32,767

10,998

ilimitado*

Sim (dois núcleos)

Windows, Mac, ou Unix

US$695

Stata/MP2

32,767

10,998

ilimitado*

Sim (quatro núcleos)

Windows, Mac, ou Unix

US$995

Stata/SE

32,767

10,998

ilimitado*

Não

Windows, Mac, ou Unix

US$395,00

Stata/IC

2,047

798

ilimitado*

Não

Windows, Mac, ou Unix

US$189,00

Small Stata

99

98

1200

Não

Windows, Mac, ou Unix

US$49,00**

* Número máximo de observações é limitado pela memória RAM
**Preço para a licensa anual
Fonte: http://www.stata.com

5

Fonte: http://www.stata.com

Onde aprender Stata

Infelizmente, as fontes mais completas para o aprendizado do Stata estão em inglês. Para os que não se sentem confortáveis com materiais em inglês, recomendamos o tutorial da professora Danielle Carusi do Departamento de Economia da UFF. No site da professora é possível fazer download dos slides utilizados nas aulas em que o Stata é utilizado.

Dentre os materiais em inglês, recomentamos fortemente o site da UCLA. Nele é possível encontrar explicações e exemplos dos comandos mais comuns do Stata. Porém, o tutorial cobre também comandos avançados, como modelos de regressão com variáveis dependentes limitadas e análise de amostras.

Também recomendamos alguns livros, em especial o livro de Michael N. Mitchell sobre gráficos. Para os leitores interessados em incrementar o modo de apresentação de resultados, esse livro pode ser muito útil.

Sites com tutoriais em Inglês

Sites com tutoriais em Português

Livros para aprender Stata

Introdução ao Stata

Variáveis Dependentes Limitadas

Introdução a Programação em Stata

Livro de Gráficos (extremamente útil e intuitivo)

Resumo

Pontos Positivos

Pontos Negativos

User Friendly: interface gráfica; sintaxe amigável; manual completíssimo. Elevado valor para aquisição
Comunidade ativa e resposta ao usuário rápida. Menos opções de arquivos complementares quando comparado ao R.
Possibilidade de análises em processamento paralelo.

Aprendendo LaTeX utilizando o writeLaTeX – I

Image

O LaTeX é um sistema para produção de documentos, sobretudo técnicos e científicos. Ao contrário de softwares como o MS Word ou o OpenOffice Writer, o LaTeX permite que você não se preocupe muito com a aparência ou tipografia do texto, mas se concentre apenas no conteúdo. Ao contrário desses outros softwares, em que você, em todo o momento, deve especificar os parâmetros do documento (fontes, parágrafos, etc), no LaTeX, isso é feito através de códigos do tipo “\section{Introdução}”.

Este tutorial oferece algumas dicas de LaTeX para iniciantes, utilizando um serviço virtual, o writeLaTeX (http://writelatex.com).

Esse site é muito interessante, porque permite que você trabalhe com LaTeX pela própria web, sem ter de instalar compiladores em seu computador. Ao mesmo tempo, a página oferece um output, que mostra em tempo real as modificações do seu documento e avisa se há erros no código.

Além disso, o WriteLaTeX traz formatos pré-prontos de artigos e slides em que você pode modificar. Por exemplo, abra o site e clique em “Start Writing – Create a New Paper”. Abrirá uma tela similar a seguinte:

Image

No lado esquerdo, há o código. No lado direito, uma prévia de como ficará o documento. A primeira linha “\documentclass[a4paper]{article} define o modelo que você está utilizando. No caso, article corresponde a um artigo, mas você poderia, por exemplo, trabalhar com outros formatos (letterbook, entre outros).

Uma primeira coisa que você pode fazer é trazer o documento para o português. Para isso, na terceira linha, troque “\usepackage[english]{babel}” por “\usepackage[brazil]{babel}”. Para definir o “encoding”, insira a linha “\usepackage[latin1]{inputenc}”.

Agora você pode explorar o documento. Altere os campos de \title{} e \author{} para alterar o título e o autor. O comando \maketitle, após a abertura do documento vai inseri-los no início do artigo. Assim, teremos como código inicial:


\documentclass[a4paper]{article}

\usepackage[brazil]{babel}
\usepackage[utf8x]{inputenc}

\title{Teste LaTeX}
\author{Sociais e Métodos}

Após isso, você poderá editar o conteúdo. O conteúdo de seu artigo ficará entre as linhas \begin{document} e \end{document}.

Imprima o autor e o título, previamente editados, no artigo com a linha \maketitle .Para se criar o resumo, edite o conteúdo entre as linhas \begin{abstract} e \end{abstract} .

\begin{document}

\maketitle

\begin{abstract} Este é um artigo de teste para o LaTeX.
\end{abstract}

Para editar as seções e subseções, utilize as linhas \section{título} e \subsection{título} para imprimi-las. Note que o conteúdo entre chaves representa o titulo da seção. O conteúdo inserido após as linhas será o texto do próprio artigo.

Para exemplificar, o seguinte código:

\section{Introdução}

Esta introdução serve para apenas para teste.

\section{Seção Teste}

\subsection{Subseção teste}
Esta subseção foi aberta somente para teste.

Após isso, lembre-se que o conteúdo do documento deve ser terminado com \end{document} .

O resultado final será o seguinte:

writelatex1

O LaTeX oferece muitos outros recursos para fazer tabelas, postar imagens, imprimir fórmulas matemáticas, criar bibliografias, entre outros. Em posts posteriores, vamos introduzir novos comandos para o uso desses recursos.

O próprio site do WriteLaTeX também é bastante intuitivo quanto ao uso dos recursos. Vale a pena dar uma explorada.

Mapas no R, parte 2: utilizando shapes

brasilTrabalhar com mapas ou dados georreferenciados frequentemente significa fazer uso de shapes. Em linhas gerais, shapes são de arquivos de mapas formados por polígonos geocodificados (ie., suas coordenadas são latitudes e longitudes) que delineiam os contornos de unidades espaciais de interesse, como limites municipais, estaduais etc. Basicamente o que se pretende nesses casos é “colorir” os polígonos de acordo com alguma característica ou atributo relevante. Este é o tipo de mapa que Leonardo utilizou em seus posts sobre médicos no Brasil e sobre transportes na cidade de SP.

Trabalhar com mapas como polígonos significa manusear (ao menos) três arquivos conjuntamente: um que possui a extensão .shp (que traz o desenho do mapa propriamente), outro com extensão .shx (que é um indexador de informações, para facilitar buscas) e um terceiro com extensão .dbf (que é um arquivo de banco de dados, trazendo informações e atributos das unidades espaciais – p.ex.: população do município, renda per capita etc.) O que liga esses três arquivos é um ID, único para cada unidade espacial, geralmente chamado de Geocódigo. Há uma explicação mínima sobre shapefiles na Wikipédia em português e uma explicação mais completa na versão em inglês.

O objetivo desse post é replicar este mapa simples sobre PIBs municipais, disponível no site da Wikipédia. O exemplo é de Diogo Ferrari.

Precisamos então seguir os seguintes passos:

  1. Fazer download de um shape de estados brasileiros
  2. Fazer um webscrapping para obter as informações dispostas na tabela do Wiki (e “limpar” tais informações)
  3. Definir aspectos da formatação do mapa (cores, rótulos da legenda)
  4. Parear os dados da tabela e as informações do arquivo de atributos (.dbf)
  5. Plotar

Primeiramente, carregamos os pacotes necessários:

require(XML)
require(RCurl)
require(maptools)
require(RColorBrewer)

Em seguida, para guardar os arquivos do mapa que serão baixados, criamos uma pasta e a definimos como diretório de trabalho (esse passo é opcional):

dir.create("c:/mapas/")
setwd("c:/mapas/")

Agora utilizaremos a função download.file para baixar um arquivo compactado (.zip) que contém o mapa (.shp, .shx e .dbf).  Isto está disponível no site Gismaps. Em seguida aplicamos a função unzip para extrair o conteúdo compactado. Por fim, para ler o mapa e guardá-lo num objeto do R, aplicamos a função readShapePoly do pacote maptools. Lendo o arquivo .shp já estamos implicitamente fazendo referência ao .shx e ao .dbf.

# Carregando dados do Mapa estadual
download.file("https://dl.dropboxusercontent.com/u/35964572/estados_2010.zip",destfile="estados_2010.zip")

unzip("estados_2010.zip")

mapaUF = readShapePoly("estados_2010.shp")

Nesse ponto, o mapa já é “plotável”:

plot(mapaUF)

plot_zoom

Mas só conseguimos ver seus formatos… não há cores que indiquem quaisquer informações sobre cada polígono (unidade da federação). O arquivo de atributos (.dbf) não contém ainda dados interessantes. Por isso, vamos ao passo 2: fazer o download das informações sobre PIBs estaduais.

Vamos usar a função readHTMLTable, já apresentada num post anterior.

PIBEstadual = readHTMLTable(getURL("https://pt.wikipedia.org/wiki/Anexo:Lista_de_estados_do_Brasil_por_PIB", ssl.verifypeer = FALSE),which=2)

Como praticamente toda informação baixada na internet, os dados da tabela chegam “sujos”  ou desformatados no R. É preciso fazer alguns procedimentos antes de utilizá-la:

# Mantemos apenas as colunas 3 e 4, que contém o nome da UF e seu PIB
PIBEstadual = PIBEstadual[2:nrow(PIBEstadual),3:4]

#renomeando as colunas
names(PIBEstadual)=c("UF","PIB")

#retira o símbolo '.' , que estava dividindo os milhares
PIBEstadual$PIB = gsub("\\.", "",PIBEstadual$PIB)

#substitui o nome pela sigla das UFs
PIBEstadual$UF = c('SP', 'RJ', 'MG', 'RS', 'PR', 'BA', 'SC', 'DF', 'GO', 'PE', 'ES', 'CE', 'PA', 'AM', 'MT', 'MA', 'MS', 'RN', 'PB', 'AL', 'SE', 'RO', 'PI', 'TO', 'AC', 'AP', 'RR')

# informa que os dados contidos na coluna 'PIB' são números
PIBEstadual$PIB = as.numeric(PIBEstadual$PIB)

# Divide o valor do PIB por 10.000, para que seja expresso em bilhões de reais
# (já está em R$ 1.000)
PIBEstadual$PIB = PIBEstadual$PIB/10^6

# Transforma os dados do PIB em uma variável categórica.
PIBEstadual$PIB_cat = cut(PIBEstadual$PIB, breaks=c(0,30,60,90,120,500,5000),
 labels=c('até 30','+ 30', '+ 60', '+ 90', '+ 120', '+ 500'))

Passamos ao passo 3: definir a formatação.

# Selecionamos algumas cores de uma paleta de cores do pacote RColorBrewer
paletaDeCores = brewer.pal(9, 'OrRd')
paletaDeCores = paletaDeCores[-c(3,6,8)]

# Agora fazemos um pareamento entre as faixas da variável sobre PIB (categórica) e as cores:
coresDasCategorias = data.frame(PIB_cat=levels(PIBEstadual$PIB_cat), Cores=paletaDeCores)
PIBEstadual = merge(PIBEstadual, coresDasCategorias)

Agora o passo 4: fazer o pareamento entre os dados da tabela e o mapa:

# Primeiramente, guardamos os dados do .dbf num objeto do R.
# Ele é um atributo do objeto mapaUF
mapaData = attr(mapaUF, 'data')

# Guardamos o número das linhas numa nova variável
# Esse passo é necessário pois o pareamento entre esses dados e a tabela do PIB
# muda a ordem os casos, o que prejudica, depois, a construção do mapa
mapaData$Index = row.names(mapaData)

# Mudando o nome da variável que indica a sigla dos estados
names(mapaData)[3] = "UF"

# Fundimos então as duas tabelas:
mapaData = merge(mapaData, PIBEstadual, by="UF")

# Reordenamos os dados do mapa
mapaData = mapaData[order(as.numeric(mapaData$Index)),]

# E guardamos essas informações novamente como sendo um atributo do arquivo de mapa.
attr(mapaUF, 'data') = mapaData

Agora, o passo final, plotar:


# Configurando tela (reduzindo as margens da figura)
parDefault = par(no.readonly = T)
layout(matrix(c(1,2),nrow=2),widths= c(1,1), heights=c(4,1))
par (mar=c(0,0,0,0))

# Plotando mapa
plot(mapaUF, col=as.character(mapaData$Cores))
plot(1,1,pch=NA, axes=F)
legend(x='center', legend=rev(levels(mapaData$PIB_cat)),
 box.lty=0, fill=rev(paletaDeCores),cex=.8, ncol=2,
 title='Mapa dos estados brasileiros segundo o PIB em\n2010. Em bilhões de reais:')

Pronto:

mapa.pib.estados

Aprendendo R – Tutorial interativo + Vídeos

codeschoolO R aparece muito por aqui… Um dos objetivos deste blog é popularizá-lo.  Fato: o custo inicial de aprendizagem é um pouco elevado, se comparado aos de outros softwares. Mas vencido o atrito inercial, as possibilidades analíticas são muito maiores.

Este breve post tem o objetivo de apresentar dois tutoriais. O primeiro deles é o tutorial interativo da Code School. Esse tutorial é composto de uma série de explicações e exercícios de nível básico, que podem ser concluídos muito rapidamente. O próprio site simula o prompt de comando do R — nem é preciso instalar nada. Mas tem duas limitações: 1) não chega em conteúdos de nível intermediário e 2) está em inglês (mas não há nada muito avançado). Essa segunda limitação fere um pouco do propósito deste blog, que é popularizar e apresentar materiais em português…

A segunda indicação é uma série de vídeos que está sendo produzida por um usuário do Youtube, Henrique Gomide. Parece legal também. A partir do vídeo abaixo, é possível acessar o restante da lista de reprodução. Até o momento de publicação deste post, já eram 18 pequenos vídeos.

Mapas e dados georreferenciados no R – parte 1

Existem diversos softwares especializados para trabalhar com dados geográficos, que possuem funções das mais diversas, dedicadas desde desenhar e/ou corrigir polígonos, passando por gerenciamento de bancos dados até executar complexos modelos de estatística espacial. No entanto, pouco a pouco, a comunidade de usuários do R vem implementando pacotes, funções e interfaces para lidar com sistemas de informação geográficas (GIS). Talvez ainda hoje não possibilitam todos os usos que um software como o ArcGIS. Mas conhecendo um pouco da grande velocidade e gana com que se desenvolvem pacotes para o R, é bem provável daqui a pouco isso seja revertido (se é que é verdade!).

Neste post ilustro uma forma de lidar com dados georreferenciados (latitude/longitude) e plotar mapas. O pacote a ser usado é o ggmap, que possui uma função chamada geocode. Seu uso é bem simples: basta informar um endereço postal e recebemos de volta sua latitude e longitude. Detalhe: não pode haver acentos nas vogais, nem “ç”. Os caracteres aceitos são aqueles do teclado americano. :-/

Vamos obter a latitude e a longitude do MASP, Museu de Arte de São Paulo:

require(ggmap)
geocode("Av. Paulista, 1578 - Bela Vista, Sao Paulo, Brasil")

Information from URL : http://maps.googleapis.com/maps/api/geocode/json?address=Av.+Paulista,+1578+-+Bela+Vista,+Sao+Paulo,+Brasil&sensor=false
Google Maps API Terms of Service : http://developers.google.com/maps/terms
lon lat
1 -46.65726 -23.56049

A função geocode acessa o API do Google Maps e faz uma pesquisa pelo endereço informado. Obviamente, nem todos os endereços são unívocos. Qualquer um que já utilizou o Google Maps sabe que esse aplicativo retorna um rol de alternativas em caso de múltiplas possibilidades. O resultado acima é a alternativa mais provável (e, na realidade, é a correta). Mas se for o caso, é possível acessar, dentro do objeto que é resultado dessa pesquisa, as demais alternativas. É possível também procurar vários endereços de uma vez só. Basta que cada endereço esteja gravado dentro de um elemento de um vetor de caracteres (string ou character). O uso do API, no entanto, tem uma limitação: só é possível fazer 2500 consultas por dia. Limites são muito comuns em APIs de acesso gratuito. Há planos pagos que permitem elevar aquele número.

O que fazer com a latitude e a longitude? É possível plotá-las num mapa!

No exemplo abaixo, vou até o site da prefeitura de SP, baixo o código fonte da página que contém uma lista de endereços de 13 unidades do SESC na região metropolitana (existem mais unidades… mas para o exemplo, essas bastam).

# Baixando, na página da prefeitura de SP, os endereços dos SESCs
pag_pref = readLines("http://www9.prefeitura.sp.gov.br/secretarias/smpp/sites/saopaulomaisjovem/cultura/index.php?p=25")[71]

Reparem que já extraio diretamente a linha de número 71. É exatamente nela que estão todos os endereços. Então baixar o resto era desnecessário. Mas esse conteúdo não está formatado:

pag

É preciso fazer uma limpeza do código HTML e extrair apenas o conteúdo desejado:

# Separando as linhas e removendo conteúdos desnecessários
pag_pref = unlist(strsplit(pag_pref,"<br />|/ Tel"))
pag_pref =gsub("<strong>|</strong>","",pag_pref)

# Mantém apenas as linhas que contêm a expressão "Endereço"
pag_pref = pag_pref[grep("Endereço",pag_pref)]

# Remove a expressão "Endereço"
pag_pref = gsub("Endereço: ","",pag_pref)

# Retira todos os caracteres especiais
pag_pref = gsub("[[:punct:]]", ",", pag_pref)

# Remove conteúdo desnecessário da linha 1
pag_pref = gsub("esquina com a Rua 24 de Maio","Sao Paulo, SP",pag_pref)

# Adiciona a cidade à linha 8
pag_pref[8] = paste(pag_pref[8],", Sao Paulo, SP")

# Adiciona o país a todas as linhas
pag_pref = paste(pag_pref,", Brasil")

# Remove todos os acentos
pag_pref = iconv(pag_pref, to="ASCII//TRANSLIT")

Pronto, esse é o resultado, uma lista de endereços guardada num vetor de caracteres:

pag

Agora é só aplicar a função geocode para obter todas as latitudes e longitudes:

latlon = geocode(pag_pref)

Falta o mapa.

A função get_map do pacote ggmap acessa um repositório público de mapas, o stamen, copia a imagem (geocodificada) da região desejada e a retorna como um objeto do R. Há várias opções de formatação, cores etc. E há também outros repositórios de mapas (inclusive o próprio Google Maps).

# Baixa o mapa de SP (centrado na Sé - isto pode ser alterado)
sp.map =get_map(location="Sao Paulo", zoom = 11,
 source = "stamen", maptype = "toner", color = "color")

# Transforma o arquivo de mapa em um grafico (ggplot2)
sp.map.2012 <- ggmap(sp.map, base_layer =
 ggplot(aes(x = lon, y = lat), data = latlon), extent = "device")

#Plota o resultado (os pontos dos endereços)
sp.map.2012 + geom_point(size = I(4),colour="red", alpha = 2/3)

Pronto, o mapa final é esse aqui:

plot_zoom

Mas o assunto sobre dados georreferenciados e mapas está longe de ser esgotado aqui…

Webscrapping II – Baixando tabelas com o readHTMLTable

Neste segundo post sobre webscrapping, vamos apresentar um modo mais simples de extrair informações de tabelas em sites.

No exemplo anterior, acessamos o código fonte da página e copiamos seu texto através da função readLines. Desta vez, utilizaremos um comando do pacote XML, que é bastante utilizado para coleta de dados na internet com o R.

A linguagem HTML, básica para a escrita do conteúdo web, é organizada de forma “hierárquica”. No código-fonte, todo conteúdo de uma página se localiza entre as expressões: <HTML> e </HTML>. Essas duas expressões marcam o início e o fim de uma área dentro da qual todas as demais informações estarão contidas. Podemos entendê-las como demarcando as fronteiras de um grande conjunto. O conteúdo exibido no corpo principal da página é delimitado por <BODY> e </BODY>. Dentro desse “sub-conjunto”, é possível criar um parágrafo, cujo início e fim são assinalados por <p> e </p>. Vejam esse exemplo aqui.

Ou seja, todo conteúdo HTML se localiza dentro de “nós” ou nodes. Há um nó principal que contém todos os demais — e que marca o início e o fim de qualquer site. O que nos interessa é que o conteúdo de uma tabela é delimitado por <TABLE> e </TABLE>. Neste link, vocês encontram exemplos.

Algumas funções do pacote XML conseguem compreender essa estrutura da linguagem HTML e acessar o conteúdo dos nodes determinados. A função readHTMLTable é especifica para tabelas.

Vejamos como podemos simplificar código do exemplo anterior, através do uso desse comando:

require(XML)

# Endereço básico
baseurl <- "http://www.portaldatransparencia.gov.br/servidores/OrgaoExercicio-ListaServidores.asp?CodOS=25201&DescOS=BANCO%20CENTRAL%20DO%20BRASIL&CodOrg=25201&DescOrg=BANCO%20CENTRAL%20DO%20BRASIL&Pagina=XX"

#Loop para fazer download de uma sequencia de páginas
data <- data.frame()
for (i in 1:278) { #contador: vai da página 1 à pág. 278
  print(i) #imprime na tela a página que está sendo acessada
  url <- gsub("XX", i, baseurl) #substitui a expressão XX no endereço pelo índice da página
  x <- readHTMLTable(url)[[2]]

  data <- rbind(data, x) #salva os resultados num vetor
}

data #resultado final

O comando readHTMLTable extrai todas as tabelas da página. Aplicando um índice depois do comando, é possível acessar apenas a tabela de número desejado. Nesse caso, desejávamos a tabela de número “2”, por isso, readHTMLTable(url)[[2]].

Reparem que não foi preciso aplicar funções para “limpar” o conteúdo e retirar caracteres especiais.

Bem mais simples agora.

Webscrapping I: Fazendo download de dados de tabelas em sites

[POST ATUALIZADO EM 03/09/2013 – Correções apontadas por Thiago Silva] Webscrapping é a tarefa de coletar dados pela internet e prepará-los para a análise de forma automatizada. Dados de diversos tipos: tabelas/bancos de dados, textos, mapas, informações geocodificadas, softwares… Neste primeiro post sobre o assunto, trago um exemplo bastante simples: baixar dados de uma tabela e transformá-los em um banco de dados em R. O script abaixo foi produzido por uma amiga há bastante tempo — e levemente modificado por mim, depois. O site-alvo é o seguinte: http://www.portaldatransparencia.gov.br/servidores/OrgaoExercicio-ListaServidores.asp?CodOS=25201&DescOS=BANCO%20CENTRAL%20DO%20BRASIL&CodOrg=25201&DescOrg=BANCO%20CENTRAL%20DO%20BRASIL&Pagina=1 A consulta gerou 278 páginas — 278 tabelas! Seria possível, claro, copiar e colar o resultado de todas as páginas em planilhas e formatá-las manualmente. Mas que trabalhinho chato! tabela A primeira dica é: percebam o padrão contido no endereço da página, reparem que ele termina com: …&Pagina=1 . Se substituirmos isso por &Pagina=2, somos direcionados para a próxima lista de resultados. Então o primeiro passo é tentar automatizar essa mudança de páginas. Em segundo lugar, as informações contidas na tabela são, na realidade, um texto HTML formatado. Ou seja, de alguma forma, estão contidas dentro do código-fonte do site. Para visualizar isso, basta clicar com o botão direito em qualquer lugar da página e selecionar a opção “Ver código fonte” (ou algo parecido, a depender do navegador). E lá está! Na linha 285: tabela Então como fazemos? Guardamos num objeto o endereço básico da HTML, substituindo o número-índice da página (&Pagina=1, &Pagina=2 etc.) por XX: 

# Endereço básico</span>
baseurl <- "http://www.portaldatransparencia.gov.br/servidores/OrgaoExercicio-ListaServidores.asp?CodOS=25201&DescOS=BANCO%20CENTRAL%20DO%20BRASIL&CodOrg=25201&DescOrg=BANCO%20CENTRAL%20DO%20BRASIL&Pagina=XX"

Depois, criamos uma função simples para retirar caracteres irrelevantes presentes no código-fonte que vai ser baixado: 

# Cria um função simples para retirar tabs e carateres especiais da página
cleanupHTML <- function(z) {
  z <- gsub("\t","",z) #retira tabs
  z <- gsub("  ", "",z) #retira espaços duplos
  z <- gsub("^\\s+", "", z) # retira espaços no início das linhas
  z <- gsub("\\s+$", "", z) # retira espaços no fim das linhas
  return(gsub("<(.|\n)*?>","",z)) #retira caracteres especiais e retorna o resultado
}

Agora, para acessar todas as 278 páginas, baixar o código-fonte, aplicar a função que “limpa” e retira os caracteres indesejados em todo material, podemos criar um loop. Esse loop substitui a expressão “XX” pelo número da página, acessa o endereço, copia o conteúdo num objeto de tipo texto (string / character), seleciona apenas as linhas que contém informações relevantes (onde estão os dados da tabela), aplica a função de limpeza e guarda os resultados:

#Loop para fazer download de uma sequencia de páginas
data <- NULL
    for (i in 1:278) { #contador: vai da página 1 à pág. 278
    print(i) #imprime na tela a página que está sendo acessada
    url <- gsub("XX", i, baseurl) #substitui a expressão XX no endereço pelo índice da página
    x <- readLines(url) #lê o código fonte da página número 'i'
    x <- iconv(x) #converte o sistema de codificação de caracteres para UTF-8
    # Este último comando funciona apenas para Windows.
    # No Linux, troque pelo seguinte comando:
    # x <- iconv(x, "latin1", "utf-8")

    #delimina as linhas inicial e final (onde estão contidos os dados desejados)
    start <- grep("<table summary=\"Lista de servidores por Nível da Função\">", x)
    end <- grep("<script type=\"text/javascript\" language=\"javascript\" src=\"../../Recursos/helper.js\"></script>", x)[1]

    x <- cleanupHTML(x[start:end]) #aplica a função que retira tabs e caracteres especiais
    data <- c(data, x) #salva os resultados num vetor
 }

O que produzimos até agora é um vetor de várias linhas, que contém, em cada uma delas, não somente os dados, como também alguns espaços vazios, células em branco e “restos” que não foram limpados pela função que montamos. Por isso, um trabalhinho adicional ainda é necessário. Além disso, em cada uma das páginas de resultado, o cabeçalho da tabela se repete. É melhor retirá-lo completamente e depois re-adicioná-lo, mas já como nome das variáveis do banco de dados que vamos compor:


temp <- data

#Retira espaços vazious e marcas de comentários
 temp <- temp[-which(temp %in% c("", " ", " ", " ", " ", " ", " "," ", " <!--", " -->"))]

#Retira cabeçalhos (pois foram coletados várias vezes -- uma vez em cada página)
 temp <- temp[-which(temp %in% c("CPF", "Nome do servidor", "Órgão de lotação"))]

#organiza os resultados como um Dataframe
 temp_m <- as.data.frame(matrix(temp, byrow=T, ncol=3))

#Dá nome às variáveis
 names(temp_m) = c("CPF", "Nome do servidor", "Órgão de lotação")

View(temp_m)

O resultado é uma banco de dados (dataframe) como este: tabela Existem outros meios de fazer esse mesmo procedimento no R (alguns mais simples, inclusive). Pacotes como o XML, RJSONIO, rjson, RCurl e RHTMLForms possuem funções que facilitam muito o webscrapping. No script acima usamos apenas as funções contidas nos pacotes básicos, que já vem com o R.

R+Google Ngram Viewer = Análise de milhões de livros

O Google Ngram Viewer é uma ferramenta pouco conhecida. O que ela faz é simplesmente contar palavras, mas em milhões de livros ao mesmo tempo — que são parte do acerco do Google Books. Ou seja, faz, de modo quase instantâneo, um procedimento simples de Análise de Conteúdo.

Os seus criadores são pessoas de diversas áreas de formação e que pretendem dar início a um estudo massivo sobre cultura e comportamento através de dados quantitativos presentes na internet, iniciando pelo conteúdo dos livros. Chamam essa iniciativa de CulturonomicsNão sei se é possível dizer que já foi de todo bem sucedida, mas fato é que possuem artigo na Science e na Nature, ilustrando formas de uso da ferramenta e algumas consequências analíticas.

Mas até recentemente, o uso desses dados por parte da maioria dos usuários era feito via consultas no próprio site ou então baixando uma quantidade muito massiva de dados, partidos em inúmeros arquivos… Mas recentemente foi lançado um pacote para R que acessa, busca e retorna resultados do Ngram Viewer, permitindo, depois, analisá-los normalmente com as demais ferramentas e pacotes do R.

O código abaixo instala o pacote ngramr e faz uma busca pelos termos “science” e “religion” no corpus de livros em inglês publicados entre 1800 e 2008. Como o buscador é “case-sensitive”, vários formatos de escrita foram utilizados.

#Limpando o ambiente
rm(list=ls())

# Instalando o ngramr
install.packages("ngramr")

# Carregando os pacotes necessários (disponíveis no CRAN)
require(tseries)
require(ngramr)
require(ggplot2)
#########################################################

#Delimitando um período inicial e final para a pesquisa
start = 1800
end=2008

# Faz as pesquisas no Google Ngram Viewer e salva no objeto ng
ng = ngram(c("science+Science+SCIENCE+sciences+Sciences+SCIENCES",
      "religion+Religion+RELIGION+religions+Religions+RELIGIONS"),
      year_start = start,
      year_end=end,
      smoothing=1)

# Gráfico dos resultados
ggplot(ng, aes(x=Year, y=Frequency, colour=Phrase)) +
      geom_line(size=1.1) + opts(legend.position="bottom")

O resultado é um gráfico como esse:

Rplot

Ora… os resultados são como uma série temporal. Podemos aplicar técnicas específicas pra isso então.

# Transformando o objeto em Dataframe...
ng = as.data.frame(ng)

# Transformando as variáveis em um objeto do tipo Time Series
science = ts(ng[as.numeric(ng$Phrase)==1,]$Frequency,
      start=c(start,1),end=c(end,1),freq=1)

religion = ts(ng[as.numeric(ng$Phrase)==2,]$Frequency,
      start=c(start,1),end=c(end,1),freq=1)

######################################################
# Fazendo algumas análises:

# Aparentemente, as duas séries estão bastante correlacionadas
# O que visualmente percebemos, quando observamos as tendências
# divergentes, crescente e decrescente.

# O coeficiente de pearson corrobora isso:
cor(science,religion)
[1] -0.8703351

# Mas o Dickey-Fuller mostra que as duas séries não são estacionárias
adf.test(science)
adf.test(religion)

# Mas quando first differenced, são...
adf.test(diff(science))
adf.test(diff(religion))

# Fazendo a correlação das variáveis diferenciadas:
cor(diff(science),diff(religion))
[1] -0.05208317

É… o problema “ciência-religião” não é tão trivial… 

Ferramenta legal, não? Dá pra pensar em muitos usos — e as sugestões dos seus criadores já são bastante amplas.