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.

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

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.