Google Refine + Perl
Google Refine é show. Para quem não conhece, acesse já o site oficial e assista pelo menos o primeiro vídeo. Serve para inúmeras tarefas envolvendo ETL.
Eu, no momento, uso muito para coisas simples, como carregar um CSV, eliminar outliers e salvar em JSON para carregar no MongoDB. Nada que um one-liner em Perl não pudesse fazer.
Infelizmente, a recíproca não é válida: one-liners em Perl são bem mais versáteis do que o Google Refine. Que tal integrar os dois?
- Google Refine pode ser facilmente integrado com um web-service qualquer.
- Perl transforma one-liners em webservices.
- PROFIT!!!
Como exemplo concreto, utilizarei dados georeferenciados. Digamos que preciso eliminar registros duplicatas, e uma das formas de detectar é pela proximidade física dos pontos no mapa. Google Refine não é um GIS, e não faz a menor ideia de como processar latitude/longitude. Entra o GeoDNA: um algoritmo que transforma coordenadas numéricas bidimensionais em uma string com propriedade interessante: quanto mais longo o prefixo compartilhado de dois GeoDNAs, mais perto estão as respectivas coordenadas no mapa (infelizmente, GeoDNAs com prefixos totalmente distintos também podem estar fisicamente próximos; localidades próximas ao meridiano de Greenwich são um exemplo clássico). Portanto, ordenando os registros por GeoDNA, as localidades próximas tenderão a ficar em linhas adjascentes. Para a integração, usaremos o recurso Add column by fetching URLs, clickando em header de qualquer coluna (a coluna não importa pois, de qualquer maneira, usaremos dados de duas):
No diálogo que aparecer, colamos o seguinte código (atenção para o nome das colunas com as respectivas coordenadas):
'http://127.0.0.1:3000/?lat='+ row.cells['latitude'].value +'&lon='+ row.cells['longitude'].value
Não precisamos do throttle delay, pois o nosso webservice é local. O diálogo deve ficar assim (nada de clickar em OK, ainda):
Agora, certifique-se de que você tem os módulos Mojolicious e Geo::DNA instalados, e rode no terminal:
perl -MGeo::DNA -Mojo -E 'a("/"=>sub{my$s=shift;$s->render(json=>{geocode=>Geo::DNA::encode_geo_dna($s->param("lat"),$s->param("lon"))})})->start' daemon
Se preferir, aqui está a versão "por extenso":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #!/usr/bin/env perl use Geo::DNA qw(encode_geo_dna); use Mojolicious::Lite; any '/' => sub { my $self = shift; $self->render(json => { geocode => encode_geo_dna( $self->param('lat'), $self->param('lon'), ), }); }; app->start; |
Uma vez iniciado o webservice, clique em OK lá no diálogo do Google Refine e aguarde. Mesmo sem o delay, é relativamente lento; porém o custo/benefício dessa gambiarrinha é, evidentemente, favorável
Implementação de classificador Naïve Bayes em MongoDB
O presente artigo é derivado do que escrevi para o evento Equinócio de Outono de 2012, promovido por São Paulo Perl Mongers.
Porém, ao invés de servir como uma espécie de complemento, é uma "volta às origens". O algoritmo original, desenvolvi em JavaScript puro, depois traduzi para Perl, refinei, detalhei e escrevi sobre. A intenção agora é ser mais show me the code possível, com um exemplo simples, enxuto e prático. Portanto, a teoria e os detalhes continuam lá no artigo original. Aqui, é mão na massa!
Ensinando o classificador
O classificador funciona por palavras-chave, e o MongoDB, nesse estágio, atua como um mero key/value storage. Salve o código de inicialização a seguir como init.js e carregue com mongo bayes init.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | db.bayes.update({_id: 'pelos'}, {$inc: {total: 1, 'categ.gato': 1}}, 1); db.bayes.update({_id: 'bigodes'}, {$inc: {total: 1, 'categ.gato': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.gato': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.gato': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.gato': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.gato': 1}}, 1); db.bayes.update({_id: 'rabo'}, {$inc: {total: 1, 'categ.gato': 1}}, 1); db.bayes.update({_id: 'pelos'}, {$inc: {total: 1, 'categ.cachorro': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.cachorro': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.cachorro': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.cachorro': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.cachorro': 1}}, 1); db.bayes.update({_id: 'rabo'}, {$inc: {total: 1, 'categ.cachorro': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.galo': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.galo': 1}}, 1); db.bayes.update({_id: 'bico'}, {$inc: {total: 1, 'categ.galo': 1}}, 1); db.bayes.update({_id: 'asa'}, {$inc: {total: 1, 'categ.galo': 1}}, 1); db.bayes.update({_id: 'asa'}, {$inc: {total: 1, 'categ.galo': 1}}, 1); db.bayes.update({_id: 'penas'}, {$inc: {total: 1, 'categ.galo': 1}}, 1); db.bayes.update({_id: 'bico'}, {$inc: {total: 1, 'categ.ornitorrinco': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.ornitorrinco': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.ornitorrinco': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.ornitorrinco': 1}}, 1); db.bayes.update({_id: 'pata'}, {$inc: {total: 1, 'categ.ornitorrinco': 1}}, 1); db.bayes.update({_id: 'rabo'}, {$inc: {total: 1, 'categ.ornitorrinco': 1}}, 1); db.bayes.update({_id: 'pelos'}, {$inc: {total: 1, 'categ.ornitorrinco': 1}}, 1); |
Agora, entrando em MongoDB Interactive Shell com mongo bayes, podemos ver a estrutura resultante com db.mongo.find():
{ "_id" : "bigodes", "categ" : { "gato" : 1 }, "total" : 1 } { "_id" : "bico", "categ" : { "galo" : 1, "ornitorrinco" : 1 }, "total" : 2 } { "_id" : "asa", "categ" : { "galo" : 2 }, "total" : 2 } { "_id" : "penas", "categ" : { "galo" : 1 }, "total" : 1 } { "_id" : "pata", "categ" : { "cachorro" : 4, "galo" : 2, "gato" : 4, "ornitorrinco" : 4 }, "total" : 14 } { "_id" : "rabo", "categ" : { "cachorro" : 1, "gato" : 1, "ornitorrinco" : 1 }, "total" : 3 } { "_id" : "pelos", "categ" : { "cachorro" : 1, "gato" : 1, "ornitorrinco" : 1 }, "total" : 3 }
Cada palavra-chave é associada aos contadores das categorias, além do contador geral de ocorrências. Agora, vamos processar essa estrutura de dados!
Classificador em map/reduce
O código a seguir é quase que um programa independente em JavaScript, salve-o como bayes-mongodb.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | var words = []; for (var word in doc) words.push(word); var categ = [ 'gato', 'cachorro', 'galo', 'ornitorrinco' ]; var guess = db.runCommand({ mapreduce: 'bayes', out: { inline: 1 }, query: { _id: { $in: words } }, scope: { categ: categ, doc: doc, }, map: function () { for (var i = 0; i < categ.length; i++) { var ctg = categ[i]; var prob = Math.log( typeof(this.categ[ctg]) != 'undefined' ? this.categ[ctg] : 1.18e-38 ); prob -= Math.log(this.total); emit(ctg, prob * doc[this._id]); } }, reduce: function (key, values) { var result = 0; values.forEach(function (value) { result += value; }); return result; } }); var res = { value: -Infinity }; for (var i in guess.results) if (guess.results[i].value > res.value) res = guess.results[i]; print(res._id); |
Para passar parâmetros via linha de comando, usamos a opção --eval do MongoDB Shell, e o script imprime na tela a categoria "adivinhada":
$ mongo bayes --quiet --eval 'var doc = { bigodes: 1, rabo: 1 }' bayes-mongodb.js
gato
$ mongo bayes --quiet --eval 'var doc = { bico: 1, rabo: 1, patas: 4 }' bayes-mongodb.js
ornitorrinco
$ mongo bayes --quiet --eval 'var doc = { bico: 1, asa: 2 }' bayes-mongodb.js
galo
Conclusão
O básico do básico esta aí ![]()
É evidente que os resultados e a eficiência do classificador bayesiano variam de caso para caso, e até de implementação para implementação. Se a primeira tentativa não for um sucesso, se quiser entender para que serve o "número mágico" 1.18e-38, ou se quiser automatizar o "treinamento" através de um pequeno script em Perl, consulte o artigo original!
As 10 coisas que me impressionam mais do que o Instagram

Não, essa foto não foi tirada usando o Instagram
Neste post, não falarei nada a respeito do Instagram em si. Afinal, criticá-lo é estar na contramão do sucesso.
Não, ao invés disso, irei enumerar algumas tecnologias relacionadas à imagem que me impressionaram nos últimos anos. E, é claro, Instagram não está entre elas.
1 - Memória Fotográfica Coletiva
Qualquer um munido de um celular se acha o Paganini do pincel hoje. Imagine se ostentar uma Canon 5D Mark II, então!
Pois bem, Iain M. Banks já idealizou em seu Feersum Endjinn que, caso seja possível transferir personalidade para um chip, poderíamos obter uma compressão desgraçada de boa, já que 99.99% da nossa individualidade não é tão... Individual
Enfim, lembre-se: você é especial. Assim como todos os outros. E as suas fotos são super-originais e criativas. Assim como as dos outros, claro.
2 - ONE-BIT CAMERA
Se lidar com limitações e restrições estimula a criatividade, esse seria o extremo absoluto na evolução dos filtros do Instagram.
Puxa, quando criança, sempre quis ter uma câmera Polaroid. Mas quando peguei numa câmera digital, a vontade passou na hora. Pois bem, muita gente ainda insiste nessa nostalgia. Sabem de onde vêm aquela ridícula borda branca em volta das "representações das fotos"? Aliás, já viu essa borda em uma foto qualquer? Pois é...
Enfim, se o objetivo não é mimetizar "velhos bons tempos", mas sim "conter para despertar a criatividade", por que não fotos monocromáticas? Não, não branco-e-preto; isso é mainstream demais! Só branco e preto!
3 - Microsoft Photosynth
Outro extremo: ao invés de filtrar informação, agregar.
Qualquer um com o mínimo de habilidade no Photoshop monta um panoraminha em questão de minutos. Pois bem, com Photosynth e um celular com giroscópio/bússola, passa a ser uma questão de segundos.
(infelizmente, requer o plugin do tal do Silverlight)
4 - Lytro
E que tal agregar a informação de profundidade? OK, para isso, não basta um celular; o hardware precisa ser especializado.
Mas não é nada absurdo (custa a partir de 400 Washingtons, no momento da escrita). O efeito é imprescindível: primeiro se tira a foto, e depois ajusta o foco! Isso é, a imagem toda está em foco, e você tem a liberdade de escolher o plano de profundidade de uma imagem já "congelada"! Inclusive, Lytro promete soltar uma atualização de software que permita extrair uma espécie de voxel-maps a partir das fotos tiradas com o seu equipamento. Show!
Por mim, esse feito já merece o Oscar da Paz. Veja (clickando em diversos pontos na imagem):
5 - C3 Technologies
Afastando-se um pouco da Terra... Que tal olhar o seu bairro com os olhos de um game da Blizzard?
Tá bom, isso só se aplicaria se você morasse em Oslo, Noruega, mas a tecnologia da 3D-ficação da imagem aérea é impressionante.
Sim, há rumores de que a Apple comprou esta tecnologia. É também fato comprovado de que a Nokia comprou. Instale o bendito do plugin e deleite-se: http://maps.nokia.com/40.6897386,-74.044671,17.8,293.62,60.04,3d.day
Bônus
Google Maps também tem uma renderização 3D, não tão eye-candy, mas que impressiona pelo seguinte fato:
Sim, ele está tentando renderizar as árvores como se fossem prédios. Sabe o que isso significa, meu amigo? Uma gafe dessas indica que o processo é quase que totalmente automático! Isso é: uma falha (árvores = prédios) comprova a regra (prédios = prédios). Nada mal!
6 - Urthecast
Continuando com a temática espacial... Que tal um crossover entre YouTube e Google Earth?!
Quer dizer... Todo mundo já ouviu uma piadinha sobre algum mentecapto ligando para amigo pedindo para apontar Google Earth para a própria casa, na esperança de aparecer "ao vivo"... Pois bem: ainda em 2012, isso será possível, graças ao Urthecast:
7 - Fotos coloridas tiradas em 1910
E não, não foram pintadas à mão! Sergei Mikhailovich Prokudin-Gorskii tirou fotos coloridas da Rússia no período de 1909 a 1912, valendo-se da técnica que utilizamos hoje em dia para tirar foto HDR com uma câmera comum: sucessão de imagens tiradas com parâmetros distintos. No caso dessas fotos, "parâmetro" era o filtro vermelho, azul e verde. Pois é, as fotos só puderam ser "reveladas" décadas mais tarde!
8 - Vídeo em HDR
Fotos HDR são bem comuns; até iPhone tira. Já vídeos... São os famosos timelapses, que dão uma "acelerada" no tempo. Por que isso?
Simples: para tirar uma foto HDR (High-Dynamic Range) com uma máquina comum, precisa ajustá-la para várias configurações de exposição, tirando uma série de fotos, e depois recombiná-las através de um software. O ajuste do diafragma é mecânico, logo, leva tempo, logo, não dá para chegar nem nos 12 fps.
Mas e se usar duas câmeras, pré-configuradas para exposições distintas?! Confesso que não entendo nem como funciona a exposição para filmagem, mas para dividir a imagem, saquei que usaram um espelho semi-transparente:
9 - GIFs animados fotorrealísticos!
Se para você GIFs animados são sinônimo das homepages toscas da década de 90, está na hora de rever os seus conceitos!
Apesar da limitação técnica de empregar uma paleta de 256 cores, resultados surpreendentes podem ser obtidos:
10 - Lente macro no celular
E, para fechar com a chave de ouro, não poderia faltar essa gambiarra no melhor estilo MacGyver: transforme o celular em uma câmera macro (quase um microscópio) acoplando a ele... Uma lente do cabeçote de leitura do drive de DVD :S
Aliás, a imagem lá em cima, no header desse blog, foi feita exatamente desse jeito








