Visualização de dados com Seaborn (Pt. I/II)

Realizaremos análises exploratórias de dados com informações disponibilizadas pela própria biblioteca Seaborn

Neste projeto trataremos das gorjetas. Sabemos que no Brasil as pessoas têm o costume de pagarem 10% do valor da conta como gorjeta, de forma opcional. Em outros regiões, este valor é livre e fica a critério do cliente, ou seja, não há uma porcentagem exata estabelecida.

Como não poderemos analisar as gorjetas dadas por todas as pessoas do mundo, faremos uma amostra com um grupo de pessoas ou registros na base de dados, e realizaremos Quatro Análises sobre gorjetas.

Visualização de dados com Seaborn Pt. I/II

Primeira Análise

Verificar se o valor da conta influencia no valor da caixinha e se este aumento é proporcional, gerando gráficos estatísticos.

Segunda Análise

Geraremos gráficos relativos à sobremesa, verificando as diferenças de gorjeta entre as pessoas que pediram e as que não pediram a sobremesa para criar gráficos distintos, inclusive traçando uma regressão linear.

Aplicaremos um Teste de Hipóteses, onde geramos uma hipótese nula e uma hipótese alternativa para descobrir como nossa população vai se comportar com base nestas amostras.

Visualização de dados com Seaborn Pt. II/II

Terceira Análise

A terceira análise verificará se o dia da semana possui alguma influência sobre o valor da gorjeta, produzindo vários gráficos diferentes com formatações e características visuais diversas.

Quarta Análise

Por fim, faremos a análise da hora do dia, atestando em qual horário nosso grupo dá mais ou menos caixinha. Com base nesta amostra, geramos gráficos de histograma, boxplot e etc.

Iniciando…

Chamaremos a biblioteca com para o sistema ler o arquivo , recebendo como parâmetro o nome .

import pandas as pddf = pd.read_csv('gorjetas.csv')
print(df.info(), df.shape)

Para começarmos, faremos a tradução as colunas e campos da língua inglesa para portuguesa. Começaremos a tradução relembrando quais são as colunas que possuímos. Passei as variáveis em uma Series para melhor compreensão:

pd.Series(df.columns)
Visualizando as colunas de gorjetas

Para elucidar melhor todo o exemplo, passaremos o df para gorjetas.

gorjetas = df

Na célula seguinte, criaremos uma lista chamada igual a chave e o valor de cada uma das colunas. Copiaremos o conteúdo dos colchetes de e colaremos dentro das chaves de , separando cada coluna em uma linha.

Em cada linha, passaremos o valor alvo que queremos renomear com a tradução entre aspas simples para cada ocorrência.

renomear = {
'total_bill' : 'valor-da_conta',
'tip' : 'gorjeta',
'dessert' : 'sobremesa',
'day' : 'dia_da_semana',
'time' : 'hora_do_dia',
'size' : 'total_de_pessoas'
}
gorjetas = df.rename(columns = renomear)
gorjetas.head(1)

Desta forma, poderemos visualizar nossos dados a partir de . Na célula seguinte, passamos o comando para visualizar a primeira linha de nossa tabela já com as nomenclaturas das colunas traduzidas.

Alterando valores da variável sobremesa

Porém, os conteúdos dos campos continuam em inglês, como a contendo "No" ou "Yes" e com "Dinner" ou "Lunch", por exemplo.

Começaremos com utilizando , o qual é um tipo categórico de "sim" ou "não".

Para visualizarmos todos os tipos que aparecem para este campo, inserimos o comando

gorjetas.sobremesa.unique()

Assim, recebemos o retorno com os conteúdos possíveis para este campo em específico. Copiaremos este mesmo conteúdo e criaremos uma lista do tipo sendo igual a .

Em seguida, utilizaremos para fazer a tradução da mesma forma como fizemos para cada coluna anteriormente e mapeando todos os para "Sim" e "Não" utilizando o comando recebendo o valor de .

sim_nao = {
'No' : 'Não',
'Yes' : 'Sim'
}
gorjetas.sobremesa = gorjetas.sobremesa.map(sim_nao)
gorjetas.head()

Alterando valores da variável dia_da_semana

Aplicaremos a mesma metodologia para o . Veremos quais os dias da semana que aparecem como resposta para este campo usando novamente para .

pd.Series(gorjetas.dia_da_semana.unique())

O sistema apresentará um contendo entre colchetes.

Em seguida, criaremos uma lista chamada sendo igual aos mesmos valores entre chaves separados por linhas diferentes e atribuindo as traduções corretas com .

dias = {
'Sun' : 'Domingo',
'Sat' : 'Sábado',
'Thur' : 'Quinta',
'Fri' : 'Sexta'
}
gorjetas.dia_da_semana = gorjetas.dia_da_semana.map(dias)
gorjetas.head(10)

Para termos certeza de que as ocorrências foram todas traduzidas, inseriremos

gorjetas.dia_da_semana.unique()
.unique( )

Alterando valores da variável hora_do_dia

Constatada a mudança, alteraremos o da mesma maneira. Para ver os conteúdos possíveis, escreveremos:

gorjetas.hora_do_dia.unique()

Em seguida, criaremos para atribir as devidas traduções:

hora = {
'Dinner' : 'Jantar',
'Lunch' : 'Almoço'
}
gorjetas.hora_do_dia = gorjetas.hora_do_dia.map(hora)
gorjetas.head()
.map()
.unique()

Até aqui…

  • Importamos um arquivo chamado tips.csv e armazenamos em
  • Conhecemos os dados das nossas análises
  • Traduzimos nossos dados do inglês para português

A partir de agora

Vamos fazer nossa primeira análise descritiva e visual para descobrir se dentro da nossa amostra, o valor da conta influenciou no valor da gorjeta.

Vamos gerar gráficos estatísticos para conseguirmos entender melhor o ponto das gorjetas.

Análise 1 (A conta influencia na gorjeta?)

Nossa primeira análise busca verificar se de fato o valor da conta influencia no valor da gorjeta.

Primeiramente, importaremos a nova biblioteca chamada Seaborn; criada para desenhar os gráficos estatísticos que permite a exploração da distribuição de dados.

Há uma biblioteca bastante conhecida chamada MatPlotlib, a qual gera visualizações bastante interessantes, mas para que sejam sofisticadas, é necessário inserir uma grande quantidade de código. Porém, apesar de ser em Python, não foi criada para trabalhar com o Pandas, pois surgiu dez anos antes deste.

O Seaborn surgiu neste contexto, para que possa trabalhar com em um código bem mais enxuto de maior nível.

O Seaborn que importaremos é uma API feita com base no MatPlotlib para plotagem de dados, geração de visualizações e gráficos estatísticos, além de ser em Python.

!pip install seaborn==0.9.0
import seaborn as sns
!pip show seaborn
!pip show seaborn

Começaremos nossa primeira análise relembrando quais são as nossas colunas escrevendo . Geraremos um gráfico estatístico a partir dos valores das colunas e , verificando se seu aumento ou diminuição são proporcionais.

Chamaremos o Seaborn por e geraremos este primeiro gráfico com , o qual receberá três parâmetros:

  • referente ao valor do eixo ,
  • para
  • base de dados que estamos utilizando.
sns.scatterplot(
x = 'valor_da_conta',
y = 'gorjeta',
data = gorjetas)

Poderemos armazená-la em uma variável adicionando-a antes da sentença de , fazendo com que o endereço desapareça.

valor_gorjeta = sns.scatterplot(
x = 'valor_da_conta',
y = 'gorjeta',
data = gorjetas)

Analisando este primeiro gráfico, poderemos ver as relações entre os eixos e .

Aparentemente, há uma progressão linear que indica o aumento do valor da gorjeta conforme o valor da conta é maior para cada mesa.

Para atestarmos se todos os registros estão realmente preenchidos ou são nulos, podemos utilizar:

print(
'A base de dados contém {} registros'.format(gorjetas.shape[0]))
Ao executar, veremos o sistema apontar a existência de 244 registros.

Para descobrir quantos não são nulos, pularemos uma linha na mesma célula adicionando ao final da frase anterior e criaremos um novo para . Para fazer este cálculo, usamos o método para .

print(
'A base de dados contém {} registros \n'.format(gorjetas.shape[0]))
print(
'Registros não nulos por variável:')
gorjetas.count()

Como recebemos o retorno do sistema apontando 244 registros para todos os campos, significa que todos estão preenchidos e não há nenhum dado nulo.

Portanto, nossa primeira visualização em gráfico está correta, e concluímos que à medida que o valor da conta aaumenta, o valor da gorjeta também tende a ser maior. Tomando essa hipótese, veremos se estes valores são proporcionais.

Através do comando , veremos as colunas de nosso projeto. Para criar um novo campo deste cálculo, inseriremos em passando o nome .

gorjetas['porcentagem'] = gorjetas['gorjeta'] /gorjetas['valor_da_conta']

Executando esta linha e visualizando a nova adição com , poderemos ver o novo campo .

Se quisermos trabalhar com valores arredondados para duas casas decimais, basta escrevermos sendo igual a passando o número de casas decimais que queremos trabalhar.

gorjetas.porcentagem = (gorjetas.porcentagem.round(2) * 100)

Como já temos o valor da porcentagem, queremos visualizar um gráfico estatístico com sendo o eixo e a sendo o eixo para vermos se realmente os valores são proporcionais.

Para isso, criaremos uma variável para armazenar esta visualização. Em seguida, chamaremos o Seaborn com seguido de recebendo e . Por fim, passaremos como terceiro argumento.

porcentagem_conta = sns.scatterplot(
x = 'valor_da_conta',
y = 'porcentagem',
data = gorjetas)
sns.scatterplot( )

O resultado é bastante interessante, pois visualmente parece que o valor da conta não é proporcional ao valor da gorjeta, e sim que diminui.

Ou seja, nas contas mais altas, os valores das gorjetas foram maiores do que as mais baixas, porém não são proporcionais, apesar das impressões do primeiro gráfico feito anteriormente.

Com isso, adicionaremos um comentário com esta análise, indicando que, aparentemente, os valores não são proporcionais.

Até este momento, fizemos observações interessantes com duas visualizações em gráficos estatísticos: uma chamada para vermos os valores da conta e da gorjeta, e outra das contas com as porcentagens para atestar se os valores são proporcionais. Em ambas, utilizamos o gráfico de dispersão .

Para visualizarmos a progressão que estamos tendo, escreveremos apenas o comando recebendo os mesmos eixos, sem necessidade de armazenar em uma variável própria.

sns.lmplot(
x = 'valor_da_conta',
y = 'porcentagem',
data = gorjetas)
.lmplot( )

Com este gráfico, poderemos ver que realmente o valor da porcentagem tende a diminuir com o aumento do valor total da conta.

Inserindo subtítulo

Após ler o arquivo tips.csv, é possível atribuir o gráfico a uma variável. Por exemplo:

primeiro_plot = sns.scatterplot(
x = 'total_bill',
y = 'tip',
data = dados)
sns.scatterplot( )

Porém, ao executar a variável , o resultado será o endereço de memória em que se encontra nossa imagem e não a exibição da imagem, conforme a imagem abaixo:

Para visualizar a figura armazenada na variável utilizamos a função:

primeiro_plot.get_figure()
.get_figure( )

É possível adicionar um subtítulo a um plot gerado com o código . Por exemplo, ao executar o código:

primeiro_plot.figure.suptitle('Valor da conta x Gorjeta')

Para visualizar a imagem com o subtítulo, podemos usar mais uma vez o

primeiro_plot.get_figure()
primeiro_plot.get_figure( )

Inserindo título ao gráfico

Para adicionar um título, podemos utilizar o

primeiro_plot.set_title(
'Análise do valor da gorjeta em função do valor da conta')
primeiro_plot.set_title( )

Alterar labels do gráfico

Para alterar a descrição das labels do eixo e do eixo , usamos a função

primeiro_plot.set(
xlabel = 'Valor da conta',
ylabel = 'Valor da gorjeta')
primeiro_plot.set( )

Salvando gráfico

Para salvar um gráfico e fazer download da imagem no formato png por exemplo, atribuímos o gráfico a uma variável através da função:

imagem = primeiro_plot.get_figure()

Em seguida, para salvar a imagem do gráfico usamos o comando:

imagem.savefig('imagem.png'):

Após salvar a imagem, podemos dar um refresh nos arquivos e visualizar a imagem disponível para download.

.savefig( )

Até aqui vimos:

  • Importação do Seaborn para gerar diferentes gráficos
  • Analisamos de forma visual e descritiva o valor da gorjeta em relação ao valor da conta
  • Criamos diferentes tipos de gráficos

Segunda Análise

Teste de Hipóteses (Sobremesa interfere no valor da gorjeta?)

Vamos analisar se a sobremesa alterou ou não o valor da gorjeta e realizar nosso primeiro teste de hipóteses.

Finalizamos nossa primeira análise e vimos que o valor da conta influencia no valor da gorjeta, mas não de maneira proporcional.

Temos um campo , e queremos visualizar todos as mesas que pediram sobremesa. Para isso, digitaremos e passaremos a instrução sendo para visualizar todas as pessoas que pediram.

gorjetas[gorjetas.sobremesa == 'Sim']

Desta forma, poderemos ver todas as ocorrências de “Sim” para o campo . Mas como são muitos registros, é mais interessante descrevê-los por meio do comando .

gorjetas[gorjetas.sobremesa == 'Sim'].describe()
Dos 244 registros, há 93 que pediram a sobremesa na linha de “count”.

Assim poderemos visualizar quantos pedidos atendem esta chamada. Na linha seguinte, veremos “mean” que corresponde à média de dos valores de cada campo.

Agora, faremos o mesmo processo pedindo ao sistema que descreva as informações dos clientes que não pediram a sobremesa, alterando por .

gorjetas[gorjetas.sobremesa == 'Nao'].describe()

Com isso, poderemos ver que 151 registros pediram o item, e que a média do valor da conta e da gorjeta são menores em relação à descrição anterior, mesmo que bastante próximas. Também compararemos as linhas de valores máximos e mínimos.

A seguir, faremos uma visualização com a distribuição de registros que pediram e que não pediram a sobremesa para realizarmos uma análise mais precisa.

Nesta etapa, geraremos alguns gráficos para conseguirmos saber se de fato o valor da gorjeta é influenciado com o pedido da sobremesa ou não.

sns.catplot( )

O primeiro será um gráfico categórico de “sim” ou “não” colocando todos os pontos dos clientes que pediram e dos que não pediram este item, em comparação com o valor da gorjeta em dois eixos e .

Para criar, chamaremos o Seaborn com seguido de

sns.catplot(
x = 'sobremesa',
y = 'gorjeta',
data = gorjetas)
sns.catplot( )

Este gráfico categórico retrata os grupos de pessoas que não pediram sobremesa e os que pediram, em comparação ao valor da gorjeta em cada caso.

Poderemos ver que o valor de gorjeta mais alto está no grupo que pediu a sobremesa.

Para validar, iremos à tabela de descrição das ocorrências de para o campo e veremos que o valor máximo de gorjeta foi de $10 conforme o campo de "max". Enquanto do grupo que não pediu, o valor máximo está em $9, o que confirmará os dados do gráfico.

Porém, ainda não podemos afirmar que a sobremesa influencia diretamente no valor da gorjeta; é possível ver que há diferenças, mas a resposta não é exata.

Para fazermos esta confirmação, geraremos um segundo gráfico que nos permitirá comprovar essa questão por meio de tonalidades diferentes para cada grupo.

relplot( )

Chamaremos a classe do Seaborn com seguido de . Passaremos e que irá mostrar um aumento conforme vimos anteriormente. Depois, adicionaremos um novo parâmetro do tipo e finalizaremos com .

sns.relplot(
x = 'valor_da_conta',
y = 'gorjeta',
hue = 'sobremesa', <---------
data = gorjetas)

Analisando este gráfico, poderemos visualizar que os pontos de “não” possuem uma distribuição mais linear do que os de “sim”, os quais aparentam mais espaçamento sem grande linearidade.

Criaremos este mesmo gráfico em duas colunas para cada grupo. Copiaremos e colaremos a mesma sentença anterior, passando um quarto parâmetro igual a .

sns.relplot(
x = 'valor_da_conta',
y = 'gorjeta',
hue = 'sobremesa',
col = 'sobremesa', <-----------
data= gorjetas)

No primeiro gráfico de tonalidades, tínhamos todos os plots na mesma categoria, e neste último temos cada plot em um gráfico diferente legendado.

sns.lmplot( )

Se quisermos traçar uma linha que permite uma visualização mais clara da situação, utilizaremos o já citado, passando os eixos e como e respectivamente, separando os dois grupos com em tonalidades e finalizando com .

sns.lmplot(
x = 'valor_da_conta',
y = 'gorjeta',
col = 'sobremesa', <--------
hue = 'sobremesa', <--------
data = gorjetas)

Com isso, poderemos ver que existe uma diferença de fato; visualmente, identificaremos que aqueles que pediram sobremesa possuem a distribuição diferente em comparação aos que não pediram.

Cálculo Estatístico

Agora, precisaremos constatar se os dois casos realmente influenciam no valor da gorjeta. Isso só será possível através de um cálculo estatístico.

Antes, geraremos mais um gráfico; estamos sempre utilizando um valor da conta e outro de gorjeta, e sabemos que quando um aumenta o outro também aumenta, ainda que sem uma proporção exata. Copiaremos a última sentença escrita e colaremos na célula seguinte, substituindo por no eixo .

sns.lmplot(
x = 'valor_da_conta',
y = 'porcentagem',
col ='sobremesa',
hue = 'sobremesa',
data = gorjetas)

Desta forma, conseguiremos visualizar que o grupo que não pediu sobremesa possuirá uma linha mais horizontal, enquanto os que pediram apresentará maior inclinação da linha.

Visualmente, parece que existe uma diferença na gorjeta dependendo se a sobremesa é pedida ou não.

sns.relplot( )

Em todos os gráficos, utilizamos pontos e linhas. Mas em há um parâmetro capaz de transformar totalmente em linhas com uso, chamado do tipo .

sns.relplot(
x = 'valor_da_conta',
y = 'gorjeta',
col = 'sobremesa',
hue = 'sobremesa',
kind = 'line',
data = gorjetas)

Nossa conclusão será que, de fato, existe uma diferença no valor da gorjeta de acordo com o pedido ou não de sobremesa, mas que é bastante sutil. Para termos certeza da influência de um sobre o outro, faremos um cálculo estatístico a seguir.

Os grupos que pediram e que não pediram sobremesa se comportam de formas diferentes quando se trata do valor de gorjeta. Utilizamos os dados de uma amostra, pois não podemos verificar o comportamento de todas as pessoas do mundo e colocar os dados dentro de nosso plot.

Porém, precisaremos pensar se os clientes que futuramente virão ao nosso restaurante se comportarão da mesma maneira ou não. Vimos antes que, adicionando , as diferenças entre os grupos ficam bem mais visíveis.

Neste passo, descobriremos se a população geral que consome a sobremesa e a que não consome se comportam igual ou diferentemente da amostra através de testes de hipótese.

Hipótese nula H0 (gorjeta é igual nos dois grupos)

Nossa hipótese nula pressupõe que a distribuição da gorjeta é igual nos dois grupos.

Hipótese alternativa H1 (gorjeta é diferente para os grupos)

"A distribuição da taxa da gorjeta não é a mesma nos dois grupos".

Biblioteca Ranksums

Para realizarmos nosso teste de hipóteses, importaremos a biblioteca Ranksums a partir de .

from scipy.stats import ranksums

Agora, traremos o valor da porcentagem de todas as pessoas que pediram a sobremesa a partir da montagem de uma .

Na célula seguinte, escreveremos recebendo sendo a entre aspas. Lembramos que o e devem estar escritos exatamente como implementamos na base de dados. Em seguida, adicionamos que queremos verificar.

gorjetas.query("sobremesa == 'Sim'").porcentagem
gorjetas.query( )

O sistema retorna uma lista com todos os valores relativos ao nosso comando. Para deixarmos mais legível, atribuiremos todo o resultado da em uma variável .

sobremesa = gorjetas.query("sobremesa == 'Sim'").porcentagem

Faremos a mesma operação para identificar o resultado nos casos dos clientes que não pediram sobremesa, armazenando em uma variável e substituindo por .

sem_sobremesa = gorjetas.query("sobremesa == 'Não'").porcentagem

Agora que já temos os dois grupos para nossa análise, verificaremos as hipóteses através de , passando os dois grupos.

ranksums(sobremesa, sem_sobremesa)

Valor-p com ranksums

Por enquanto, a resposta que nos importará é o de arredondados , o que significará que, apesar de os dois grupos terem dado gorjetas de valores diferentes entre si, a diferença é insignificante do ponto de vista matemático. Ou seja, não poderemos dizer que a população geral será diferente também.

Cientes disso, continuaremos somente com hipótese nula H0, não aceitando mais a hipótese alternativa. Para aceitarmos esta última, o valor de deveria ser igual ou menor do que . Como nosso resultado foi maior do que isso, poderemos descartar esta hipótese alternativa de que as gorjetas são diferentes para os grupos que pediram ou não pediram sobremesa.

Para melhorarmos a exibição em nosso relatório, atribuíremos este valor em uma variável e geraremos um registro com , recebendo seguido de

r = ranksums(sobremesa, sem_sobremesa)
print('O valor do p-value é {}'.format(r.pvalue))

Feito isso, teremos o valor de forma visivelmente mais agradável. Copiaremos e colaremos abaixo deste resultado a descrição da hipótese nula em uma célula textual para deixar bem claro.

No nosso projeto, realizamos o teste de hipótese em relação a porcentagem da gorjeta dada pelas pessoas que pediram e não pediram sobremesa e encontramos o seguinte valor:

O que esse valor do representa?

Representa que não temos evidências suficiente para aceitar a hipótese alternativa.

O p-value representa a probabilidade daquela amostra ter acontecido dentro da população. Se a chance é pequena, geralmente , ou seja, menor do que 5%, representando que um evento muito raro aconteceu, então optamos por descartar a hipótese nula, e dizer que ela pode não ser verdade.

Nessa segunda análise:

  • Fizemos uma análise descritiva das pessoas que pediram sobremesa e não pediram sobremesa com a função
  • Geramos gráficos alterando a tonalidade com parâmetro
  • Categorizamos uma informação em gráficos diferentes com o parâmetro
  • Realizamos o teste de hipótese, analisando a porcentagem da gorjeta daqueles que pediram e não pediram sobremesa

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Veremos na Visualização de dados com Seaborn pt. II/II:

Analisaremos se os dias da semana alteraram o valor da gorjeta, através de uma análise visual gerando diferentes gráficos para cada dia, explorando também de forma descritiva, levantando também uma nova hipótese em relação aos dias mais lucrativos da semana.

Obrigado.

Composing a repository of books (i bought), authors (i follow) & blogs (direct ones) for my own understanding.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store