Eu poderia dizer que foram as limitações dos utilitários de SQL e planilha de cálculo no Linux que me fizeram aprender a usar o IPython Notebook. Mas a verdade é que quando vi uma pessoa trabalhando com esse ambiente, eu pensei: “Preciso aprender isso!”.

A dificuldade e o tempo que se perde entre o rodar uma query em um banco de dados, exportar o resultado para um arquivo csv, abrir esse arquivo com a planilha de cálculos, pivotar, calcular alguns percentuais, algumas séries acumuladas, para eventualmente gerar um gráfico. Torna-se bastante trabalhoso. Arquivo daqui para lá, arquivo de lá para cá.

E quando você precisa juntar, sei lá, alguns milhões de eventos que estão em um arquivo texto, e ver quais deles não estão no banco de dados? E a partir desse filtro, fazer para cada evento desses uma chamada de API, guardar o resultado da chamada, extrair um valor lá de dentro e gravar em algum lugar? Esse tipo de coisa já me deu muita dor de cabeça para executar. Agora não dá mais.

A maneira de se trabalhar com os notebooks te leva a documentar o passo a passo de uma investigação, ou de um procedimento operacional complicado, de uma maneira reproduzível. Isso facilita demais a documentação e a automação, e te deixa a possibilidade de a cada passo e a cada resultado parcial, tomar outras linhas de investigação.

Operações antes só encontradas nas planilhas de cálculo, como pivot tables, agora podem ser feitas dentro deste ambiente computacional, em que se pode também fazer queries em bancos de dados e com muita facilidade, obter datasets a partir das mais diversas fontes, e combinar tudo com tudo.

Quem quiser começar a usar o Jupyter Notebook, o caminho mais recomendado para a instalação é o bundle Anaconda (https://conda.io/docs/user-guide/install/index.html). Com o Anaconda instalado, o Jupyter Notebook pode ser iniciado da linha de comando:

> jupyter notebook

O comando carrega um servidor web e te direciona para um navegador na porta padrão, onde você pode abrir seu notebooks e criar novos notebooks.

O Pandas é a biblioteca de obtenção e manipulação de datasets, e seus recursos são simplesmente fantásticos. Na base do Pandas está o Numpy.

O Numpy e o Pandas fazem parte do SciPy, um ecossistema baseado em Python, de software open-source para a área matemática, científica, e de engenharia. Uma das principais funcionalidades providas pelo Numpy, é o suporte a operações em matrizes n-dimensionais com alta performance.

Vamos exemplificar. Vamos criar uma matriz com 4 linhas e 3 colunas, contendo os números pares de 0 a 12, e então vamos multiplicar essa matriz por 2:

array([[ 0, 2,   4],

[ 6, 8, 10],

[12, 14, 16],

[18, 20, 22]])

A operação anterior alterou o array mat, pois houve uma atribuição. A operação seguinte vai só aparecer no console de resultado, mas não vai alterar o valor da variável:

mat * 10

array([[ 0,   20, 40],

[ 60, 80, 100],

[120, 140, 160],

[180, 200, 220]])

Aqui estamos fazendo uma subtração entre matriz e vetor (matriz unidimensional):

mat – mat[0]

array([[ 0, 0,   0],

[ 6, 6,   6],

[12, 12, 12],

[18, 18, 18]])

Aqui estamos multiplicando uma matriz por uma matriz (no caso ela mesma):

mat ** 2

array([[ 0,     4, 16],

[ 36, 64, 100],

[144, 196, 256],

[324, 400, 484]])

Aqui subtraímos para obter a diferença entre cada valor e a média dos valores:

mat – mat.mean()

array([[-11., -9.,   -7.],

[ -5., -3.,   -1.],

[ 1.,     3.,   5.],

[ 7.,     9., 11.]])

Aqui fazemos a transposição dos eixos, entre linhas e colunas:

np.transpose(mat)

array([[ 0, 6, 12, 18],

[ 2, 8, 14, 20],

[ 4, 10, 16, 22]])

No Pandas, um dataset é representado por um DataFrame, que é um conjunto de Series  com um índice em comum. Series são vetores (numpy arrays) também indexados por um Index.

DataFrames são obtidos através de algum dos métodos read (read_csv, read_json, read_excel, read_sql, etc), são combinados com concatenação, join ou merge, agrupados, pivotados, gerando novos DataFrames.

Para começar a brincar um pouco com o Pandas, vamos carregar um arquivo texto com dados do IBGE. O arquivo pib_municipios_2010-2015.txt está disponível para download aqui. Vamos começar importando todas as libs que vamos precisar aqui, e carregando um DataFrame com esses dados:

 

(33405, 22)

Existem várias funções que analisam o seu DataFrame e descrevem suas características, entre elas describe:

E também as Series que compõem o DataFrame. Por exemplo, o método value_counts:

Podemos também analisar as primeiras ou últimas linhas do DataFrame com head e tail, mas isso é muito pouco perto do que se pode fazer com os métodos de indexação disponíveis. Vamos verificar alguns:

.loc(lines, columns): método versátil para indexar linhas e colunas pelos valores

.iloc(lines, columns): método versátil para indexar linhas e colunas pelos índices

Podemos aplicar filtros usando expressões lógicas, por exemplo:

Como podemos ver o DataFrame contém dados de vários anos:

2015     5570

2014   5570

2013   5570

2012   5565

2011   5565

2010   5565

Name: Ano, dtype: int64

Como cada operação retorna um DataFrame podemos encadear diversas operações em sequência:

2010     3.088598e+07 100.000000

2011   3.622905e+07 117.299358

2012   3.941962e+07 127.629511

2013   4.444641e+07 143.904837

2014   4.543929e+07 147.119493

2015   4.632752e+07 149.995339

Caramba, o PIB de Campinas cresceu 50% de 2010 a 2015! Inacreditável!

Antes de seguir em frente, vamos descartar os anos anteriores, deixando apenas o ano de 2015 carregado no DataFrame identificado como df_pib:

(5570, 22)

Agora vamos carregar um outro DataFrame df_ddd com dados de DDD, latitude e longitude dos municípios. O arquivo ibge_city.csv pode ser baixado aqui.

 

(5570, 11)

Vamos combinar os dois DataFrames, o campo CodMunicipio do primeiro equivale ao campo iso do segundo:

Finalmente vamos agrupar o DataFrame resultante por DDD:

O resultado trouxe o que queríamos, ou seja, população e PIB por setor, de cada DDD. Ao invés de usar o PIB em valores absolutos, vamos calcular o peso relativo de cada setor da economia e retirar mais uma vez do DataFrame o que não nos interessa:

Com relação ao PIB e população também, vamos calcular quanto cada DDD representa do total, e acrescentar ainda a relação PIB/população, ou seja, a renda per capita:

Agora que o DataFrame está pronto, podemos começar fazer perguntas. Por exemplo: vamos visualizar os DDDs na medida de quanto eles representam do PIB e da população.

Imagem1.png

Vamos analisar também a distribuição dos valores de população, PIB e renda per capita, e as relações entre elas:

Imagem2

Temos uma representação do perfil sócio econômico dos DDDs através dos dados do PIB por setor da economia:

 

ImagemX.png

E uma relação dos DDDs do maior PIB até os menores, em que vemos o PIB absoluto acumulado:

ImagemY.png

Vamos carregar agora uma base de número de acessos da Anatel, para enriquecer a análise, link para o arquivo aqui:

As informações estão mês a mês. Vamos pegar apenas uma informação, a primeira de cada ano:

E agora agrupar por grupos de DDD (1X a 9X):

Vou gerar agora outro DataFrame com a informação de quanto cada grupo de DDD representa do total de acessos:

E vamos juntar agora, o % da população, o % do PIB, o % dos acessos e o crescimento ou diminuição no período entre 2010 e 2015:

Imagem5

Podemos verificar a correlação entre todas as colunas com um simples comando:

Imagem6

O interessante agora seria carregar alguns dados da nossa base de assinantes para cruzar com as características obtidas dos DDDs. Selecionamos as 30 maiores aplicações e contamos o número de assinantes, número de transações e valor total das transações, agrupando pelo DDD: SUBSTRING(phone,3,2) as “DDD”.

Em seguida calculamos quanto cada DDD representa do total (de assinaturas ou billing), adicionando uma série de dados:

Agora podemos comparar o quanto cada DDD representa do PIB, da população e a participação em nossa base de assinantes. Podemos fazer análise de cada DDD e da cada aplicação. Agrupando por grupos de DDD (1X a 9X) temos:

Imagem7

Um dos pontos em vermelho no mapa, indica alta concentração da base de assinantes de uma determinada operadora nos DDDs iniciados com 8. Analisando em detalhes os DDDs que puxam essa concentração no DDD 8X vemos por exemplo no DDD 85, de Fortaleza/CE:

População: 2,2% do total
PIB: 1,49% do total

Tem grande participação em vários produtos, principalmente de duas operadoras:

CARRIER_5 1186     17.894836
CARRIER_4 972     16.121420
CARRIER_4 1186     11.651738
CARRIER_4 1095     9.672342
CARRIER_4 1053     8.954331Name: 85, dtype: float64

Agora para finalizar, vamos calcular a renda per capita estimada dos assinantes de cada aplicação, com base na participação de cada DDD na base de assinantes:

ImagemZ.png

Será que faz sentido esse cálculo? Podemos fazer uma validação observando a correlação entre esses valores de renda per capita estimada e o valor tarifado por usuário em determinado período.

Quanto maior a renda per capita do DDD (eixo Y), em geral temos um maior valor gerado por assinatura nesse período (eixo X). Para algumas operadoras e aplicações a relação aparece, enquanto em outras nem tanto.

Imagem10

Esse é o tipo de análise que o ambiente permite e com muita facilidade podemos compartilhar os notebooks.

Esse tipo de análise inicial sobre os dados pode servir sempre de ponto de partida para ideias para testar na área de machine learning. A integração com muitas libs de IA e ML no Python já são nativas do Anaconda, e formatar os dados para treinar modelos de machine learning é muito fácil. As possibilidades são infinitas.

O passo a passo que fica documentado no notebook pode ser exportado para um script Python, que pode ser executado manualmente ou automaticamente. A organização facilita a reprodução posterior de roteiros de investigação.

Cada passo dessa investigação abre um leque de questões e possibilidades de aprofundar os temas.

Análise geográfica pelo DDD pode fazer sentido em alguns contextos por identificar características econômicas e culturais comuns, por exemplo, testes A/B de fraseologia e de interface com o usuário.

O mais legal de pensar em termos geográficos, em DDD, é que pode viabilizar estratégias locais de serviços e de mídia.

Também podemos pensar que grupos do mesmo DDD estão sujeitos a mais problemas técnicos comuns. Talvez, por isso seja uma categoria de análise de reclamações, churning  (ou cancelamento voluntário pelo usuário), erros da operadora.

Enfim, é uma das poucas informações que temos do usuário, muitas vezes dependendo da forma de interação. Não deve ser um indicativo muito preciso do público de um serviço, mas é uma média. E é uma informação que vem de graça.

Quem quiser baixar e instalar o notebook, a recomendação geral é de instalar o Anaconda, que vai gerenciar os seus ambientes. Para conectar com o SQL Server por exemplo, eu mantenho um ambiente com a versão 3.5.5 do Python.

Se quiser baixar o notebook apenas dos dados ligados ao IBGE e Anatel, segue o link:

https://github.com/Movile/Blog/blob/master/ibge-Copy1.ipynb

Software Engineer na Wavy

Posted by:Christian Janiake

Software Engineer na Wavy

Deixe seu comentário