Exploração e Visualização

Um pouco de matplotlib.

Resultados Esperados

  1. Junto com a aula passada, ferramentas simples para exploração de dados
  2. Aprender a base de Matplotlib para realizar um plot simples
  3. Aprender conceitos básicos de visualização dados
  4. Um pouco mais de filtro e seleção de dados

Sumário

  1. Introdução
  2. Análise Exploratória de Dados
    1. Dados Sintéticos
    2. Babies
  3. matplotlib
  4. Movies Dataset
  5. Base de actors
  6. Problemas de escala
  7. Dados 2d com e leitura de JSON

#In: 
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
Código de Configurar Plot (Oculto) ```python #In: plt.rcParams['figure.figsize'] = (16, 10) plt.rcParams['axes.axisbelow'] = True plt.rcParams['axes.grid'] = True plt.rcParams['axes.labelsize'] = 16 plt.rcParams['axes.linewidth'] = 2 plt.rcParams['axes.spines.bottom'] = True plt.rcParams['axes.spines.left'] = True plt.rcParams['axes.titlesize'] = 16 plt.rcParams['axes.ymargin'] = 0.1 plt.rcParams['font.family'] = 'serif' plt.rcParams['grid.color'] = 'lightgrey' plt.rcParams['grid.linewidth'] = .1 plt.rcParams['xtick.bottom'] = True plt.rcParams['xtick.direction'] = 'out' plt.rcParams['xtick.labelsize'] = 16 plt.rcParams['xtick.major.size'] = 12 plt.rcParams['xtick.major.width'] = 1 plt.rcParams['xtick.minor.size'] = 6 plt.rcParams['xtick.minor.width'] = 1 plt.rcParams['xtick.minor.visible'] = True plt.rcParams['ytick.direction'] = 'out' plt.rcParams['ytick.labelsize'] = 16 plt.rcParams['ytick.left'] = True plt.rcParams['ytick.major.size'] = 12 plt.rcParams['ytick.major.width'] = 1 plt.rcParams['ytick.minor.size'] = 6 plt.rcParams['ytick.minor.width'] = 1 plt.rcParams['ytick.minor.visible'] = True plt.rcParams['legend.fontsize'] = 16 plt.rcParams['lines.linewidth'] = 4 plt.rcParams['lines.markersize'] = 80 ``` ```python #In: plt.style.use('tableau-colorblind10') plt.ion(); ```

Introdução

Uma parte fundamental do kit de ferramentas do cientista de dados é a visualização de dados. Embora seja muito fácil criar visualizações, é muito mais difícil produzir boas visualizações.

Existem dois usos principais para visualização de dados:

Para explorar dados;
Para comunicar dados.

Nesta aula, nos concentraremos em desenvolver as habilidades que você precisará para começar a explorar os seus próprios dados e produzir as visualizações que usaremos ao longo do curso.

Como a maioria dos tópicos que veremos, a visualização de dados é um rico campo de estudo que merece o seu próprio curso.

No entanto, vamos tentar dar uma ideia do que contribui para uma boa visualização e o que não contribui.

Análise Exploratória de Dados

Vamos iniciar explorando algumas chamadas sobre como fazer merge e tratar missing data. Alguns passos simples para a Limpeza de Dados.

Dados Sintéticos

#In: 
people = pd.DataFrame(
    [["Joey",      "blue",       42,  "M"],
     ["Weiwei",    "blue",       50,  "F"],
     ["Joey",      "green",       8,  "M"],
     ["Karina",    "green",  np.nan,  "F"],
     ["Fernando",  "pink",        9,  "M"],
     ["Nhi",       "blue",        3,  "F"],
     ["Sam",       "pink",   np.nan,  "M"]], 
    columns = ["Name", "Color", "Age", "Gender"])
people
NameColorAgeGender
0Joeyblue42.0M
1Weiweiblue50.0F
2Joeygreen8.0M
3KarinagreenNaNF
4Fernandopink9.0M
5Nhiblue3.0F
6SampinkNaNM
#In: 
email = pd.DataFrame(
    [["Deb",  "deborah_nolan@berkeley.edu"],
     ["Sam",  np.nan],
     ["John", "doe@nope.com"],
     ["Joey", "jegonzal@cs.berkeley.edu"],
     ["Weiwei", "weiwzhang@berkeley.edu"],
     ["Weiwei", np.nan],
     ["Karina", "kgoot@berkeley.edu"]], 
    columns = ["User Name", "Email"])
email
User NameEmail
0Debdeborah_nolan@berkeley.edu
1SamNaN
2Johndoe@nope.com
3Joeyjegonzal@cs.berkeley.edu
4Weiweiweiwzhang@berkeley.edu
5WeiweiNaN
6Karinakgoot@berkeley.edu
#In: 
people.merge(email, 
             how = "inner",
             left_on = "Name", right_on = "User Name")
NameColorAgeGenderUser NameEmail
0Joeyblue42.0MJoeyjegonzal@cs.berkeley.edu
1Joeygreen8.0MJoeyjegonzal@cs.berkeley.edu
2Weiweiblue50.0FWeiweiweiwzhang@berkeley.edu
3Weiweiblue50.0FWeiweiNaN
4KarinagreenNaNFKarinakgoot@berkeley.edu
5SampinkNaNMSamNaN

Como podemos tratar?

  1. Missing data nas cores?
  2. Missing data nos e-mails?
#In: 
people['Age'] = people['Age'].fillna(people['Age'].mean())
people
NameColorAgeGender
0Joeyblue42.0M
1Weiweiblue50.0F
2Joeygreen8.0M
3Karinagreen22.4F
4Fernandopink9.0M
5Nhiblue3.0F
6Sampink22.4M
#In: 
email.dropna()
User NameEmail
0Debdeborah_nolan@berkeley.edu
2Johndoe@nope.com
3Joeyjegonzal@cs.berkeley.edu
4Weiweiweiwzhang@berkeley.edu
6Karinakgoot@berkeley.edu

Voltando para os dados de nomes.

Babies

#In: 
df = pd.read_csv('baby.csv')
df.head()
IdNameYearGenderStateCount
01Mary1910FAK14
12Annie1910FAK12
23Anna1910FAK10
34Margaret1910FAK8
45Helen1910FAK7
#In: 
cols = ['Year', 'Count']
df[cols]
YearCount
0191014
1191012
2191010
319108
419107
.........
564742120145
564742220145
564742320145
564742420145
564742520145

5647426 rows × 2 columns

#In: 
(df[cols].groupby('Year').
 sum().
 tail())
Count
Year
20103116548
20113079145
20123073858
20133066443
20143113611
#In: 
series = df[cols].groupby('Year').sum()
#In: 
series.plot(figsize=(18, 10), fontsize=16, lw=5)
plt.xlabel('Year')
plt.ylabel('Num Births');

png

Pequena gambiarra abaixo, vou colocar cada ano no formato 1-1-ANO. Assim o pandas sabe criar uma data.

#In: 
new_series = series.copy()
['1-1-{}'.format(str(x)) for x in new_series.index][0]
'1-1-1910'

Depois vou criar um novo índice

#In: 
dates = pd.to_datetime(['15-6-{}'.format(str(x)) for x in new_series.index])
new_series.index = pd.DatetimeIndex(dates)
new_series.head()
/tmp/ipykernel_94185/1159144160.py:1: UserWarning: Parsing dates in DD/MM/YYYY format when dayfirst=False (the default) was specified. This may lead to inconsistently parsed dates! Specify a format to ensure consistent parsing.
  dates = pd.to_datetime(['15-6-{}'.format(str(x)) for x in new_series.index])
Count
1910-06-15516318
1911-06-15565810
1912-06-15887984
1913-06-151028553
1914-06-151293322
#In: 
new_series.resample('10Y').sum()
Count
1910-12-31516318
1920-12-3115177269
1930-12-3121345615
1940-12-3119870213
1950-12-3128952157
1960-12-3138165483
1970-12-3134817092
1980-12-3129009332
1990-12-3132787986
2000-12-3133042354
2010-12-3132866450
2020-12-3112333057
#In: 
new_series.resample('10Y').mean().plot(figsize=(18, 10), fontsize=16, lw=5)
plt.xlabel('');

png

matplotlib

Existe uma grande variedade de ferramentas para visualizar dados.

Nós usaremos a biblioteca matplotlib, que é amplamente utilizada (embora mostre sua idade).

Se você estiver interessado em produzir visualizações interativas elaboradas para a Web, provavelmente não é a escolha certa, mas para gráficos de barras simples, gráficos de linhas e diagramas de dispersão, funciona muito bem.

Em particular, estamos usando o módulo matplotlib.pyplot.

Em seu uso mais simples, o pyplot mantém um estado interno no qual você constrói uma visualização passo a passo.

Quando terminar, você poderá salvá-lo (com savefig()) ou exibi-lo (com show()).

Vamos iniciar com duas listas simples de dados.

#In: 
anos = [1950, 1960, 1970, 1980, 1990, 2000, 2010]
pib = [300.2, 543.3, 1075.9, 2862.5, 5979.6, 10289.7, 14958.3]
#In: 
# cria um gráfico de linhas, com os anos no eixo x e o pib no eixo y
fig, ax = plt.subplots()

ax.plot(anos, pib)

# Adiciona um título
ax.set(
    title = 'Nominal GDP',
    xlabel = 'Ano',
    ylabel = 'USD em Bilhões'
);

png

Podemos também usar vetores numpy sem problemas

#In: 
x = np.linspace(-100, 100, 100) * 0.1
x
array([-10.        ,  -9.7979798 ,  -9.5959596 ,  -9.39393939,
        -9.19191919,  -8.98989899,  -8.78787879,  -8.58585859,
        -8.38383838,  -8.18181818,  -7.97979798,  -7.77777778,
        -7.57575758,  -7.37373737,  -7.17171717,  -6.96969697,
        -6.76767677,  -6.56565657,  -6.36363636,  -6.16161616,
        -5.95959596,  -5.75757576,  -5.55555556,  -5.35353535,
        -5.15151515,  -4.94949495,  -4.74747475,  -4.54545455,
        -4.34343434,  -4.14141414,  -3.93939394,  -3.73737374,
        -3.53535354,  -3.33333333,  -3.13131313,  -2.92929293,
        -2.72727273,  -2.52525253,  -2.32323232,  -2.12121212,
        -1.91919192,  -1.71717172,  -1.51515152,  -1.31313131,
        -1.11111111,  -0.90909091,  -0.70707071,  -0.50505051,
        -0.3030303 ,  -0.1010101 ,   0.1010101 ,   0.3030303 ,
         0.50505051,   0.70707071,   0.90909091,   1.11111111,
         1.31313131,   1.51515152,   1.71717172,   1.91919192,
         2.12121212,   2.32323232,   2.52525253,   2.72727273,
         2.92929293,   3.13131313,   3.33333333,   3.53535354,
         3.73737374,   3.93939394,   4.14141414,   4.34343434,
         4.54545455,   4.74747475,   4.94949495,   5.15151515,
         5.35353535,   5.55555556,   5.75757576,   5.95959596,
         6.16161616,   6.36363636,   6.56565657,   6.76767677,
         6.96969697,   7.17171717,   7.37373737,   7.57575758,
         7.77777778,   7.97979798,   8.18181818,   8.38383838,
         8.58585859,   8.78787879,   8.98989899,   9.19191919,
         9.39393939,   9.5959596 ,   9.7979798 ,  10.        ])
#In: 
plt.figure(figsize=(18, 10))

plt.plot(x, np.sin(x), label='Sine')
plt.plot(x, np.cos(x), label='Cosine')
plt.legend()

ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)
plt.show()

png

#In: 
plt.plot(np.sin(x), np.cos(x))
plt.xlabel('Sine')
plt.ylabel('Cosine')
Text(0, 0.5, 'Cosine')

png

Movies Dataset

Vamos olhar para mais ou menos 50 atores e algumas métricas dos filmes que os mesmos fazem parte. Em particular, vamos iniciar explorando uma forma de visualizar dados que é o histograma.

#In: 
df = pd.read_csv('https://media.githubusercontent.com/media/icd-ufmg/material/master/aulas/04-EDA-e-Vis/top_movies.csv')
#In: 
df.head(6)
TitleStudioGrossGross (Adjusted)Year
0Star Wars: The Force AwakensBuena Vista (Disney)9067234189067234002015
1AvatarFox7605076258461208002009
2TitanicParamount65867230211786279001997
3Jurassic WorldUniversal6522706256877280002015
4Marvel's The AvengersBuena Vista (Disney)6233579106688666002012
5The Dark KnightWarner Bros.5348584446477616002008
#In: 
mean_g = df.groupby('Studio').mean()['Gross (Adjusted)']
/tmp/ipykernel_94185/4007597827.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.mean is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  mean_g = df.groupby('Studio').mean()['Gross (Adjusted)']
#In: 
mean_g.sort_values()[::-1].plot.bar(edgecolor='k');

png

#In: 
plt.figure(figsize=(18, 10))
mean_g = df.groupby('Studio').mean()['Gross (Adjusted)']
mean_g /= 1e8
ax = mean_g.sort_values()[::-1].plot.bar(fontsize=20, edgecolor='r')
plt.ylabel('Média de Lucro em Milhões')
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)
/tmp/ipykernel_94185/25471854.py:2: FutureWarning: The default value of numeric_only in DataFrameGroupBy.mean is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  mean_g = df.groupby('Studio').mean()['Gross (Adjusted)']

png

#In: 
mean_g = df.groupby('Studio').mean()['Gross (Adjusted)']
mean_g /= 1e8
plt.figure(figsize=(18, 10))
plt.hist(mean_g, edgecolor='k')
plt.xlabel('Millions of Dollars')
plt.ylabel('# of Studios')

ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)
/tmp/ipykernel_94185/2013192506.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.mean is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  mean_g = df.groupby('Studio').mean()['Gross (Adjusted)']

png

Base de actors

#In: 
df = pd.read_csv('https://media.githubusercontent.com/media/icd-ufmg/material/master/aulas/04-EDA-e-Vis/actors.csv')
df
ActorTotal GrossNumber of MoviesAverage per Movie#1 MovieGross
0Harrison Ford4871.741118.8Star Wars: The Force Awakens936.7
1Samuel L. Jackson4772.86969.2The Avengers623.4
2Morgan Freeman4468.36173.3The Dark Knight534.9
3Tom Hanks4340.84498.7Toy Story 3415.0
4Robert Downey, Jr.3947.35374.5The Avengers623.4
5Eddie Murphy3810.438100.3Shrek 2441.2
6Tom Cruise3587.23699.6War of the Worlds234.3
7Johnny Depp3368.64574.9Dead Man's Chest423.3
8Michael Caine3351.55857.8The Dark Knight534.9
9Scarlett Johansson3341.23790.3The Avengers623.4
10Gary Oldman3294.03886.7The Dark Knight534.9
11Robin Williams3279.34966.9Night at the Museum250.9
12Bruce Willis3189.46053.2Sixth Sense293.5
13Stellan Skarsgard3175.04373.8The Avengers623.4
14Anthony Daniels3162.97451.8Star Wars: The Force Awakens936.7
15Ian McKellen3150.431101.6Return of the King377.8
16Will Smith3149.124131.2Independence Day306.2
17Stanley Tucci3123.95062.5Catching Fire424.7
18Matt Damon3107.33979.7The Martian228.4
19Robert DeNiro3081.37939.0Meet the Fockers279.3
20Cameron Diaz3031.73489.2Shrek 2441.2
21Liam Neeson2942.76346.7The Phantom Menace474.5
22Andy Serkis2890.623125.7Star Wars: The Force Awakens936.7
23Don Cheadle2885.43484.9Avengers: Age of Ultron459.0
24Ben Stiller2827.03776.4Meet the Fockers279.3
25Helena Bonham Carter2822.03678.4Harry Potter / Deathly Hallows (P2)381.0
26Orlando Bloom2815.817165.6Dead Man's Chest423.3
27Woody Harrelson2815.85056.3Catching Fire424.7
28Cate Blanchett2802.63971.9Return of the King377.8
29Julia Roberts2735.34265.1Ocean's Eleven183.4
30Elizabeth Banks2726.33577.9Catching Fire424.7
31Ralph Fiennes2715.33675.4Harry Potter / Deathly Hallows (P2)381.0
32Emma Watson2681.917157.8Harry Potter / Deathly Hallows (P2)381.0
33Tommy Lee Jones2681.34658.3Men in Black250.7
34Brad Pitt2680.94067.0World War Z202.4
35Adam Sandler2661.03283.2Hotel Transylvania 2169.7
36Daniel Radcliffe2634.417155.0Harry Potter / Deathly Hallows (P2)381.0
37Jonah Hill2605.12989.8The LEGO Movie257.8
38Owen Wilson2602.33966.7Night at the Museum250.9
39Idris Elba2580.62699.3Avengers: Age of Ultron459.0
40Bradley Cooper2557.725102.3American Sniper350.1
41Mark Wahlberg2549.83670.8Transformers 4245.4
42Jim Carrey2545.22794.3The Grinch260.0
43Dustin Hoffman2522.14358.7Meet the Fockers279.3
44Leonardo DiCaprio2518.325100.7Titanic658.7
45Jeremy Renner2500.321119.1The Avengers623.4
46Philip Seymour Hoffman2463.74061.6Catching Fire424.7
47Sandra Bullock2462.63570.4Minions336.0
48Chris Evans2457.823106.9The Avengers623.4
49Anne Hathaway2416.52596.7The Dark Knight Rises448.1
#In: 
plt.figure(figsize=(18, 10))
plt.hist(df['Average per Movie'], edgecolor='k')
plt.xlabel('Média por Filme')
plt.ylabel('Número de linhas')
ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)

png

Obtendo a densidade de pontos em cada barra. Note que isto nem sempre vai traduzir para um valor entre [0, 1]. Depende do eixo-x.

#In: 
plt.figure(figsize=(18, 10))
plt.hist(df['Average per Movie'], edgecolor='k')
plt.xlabel('Média por Filme')
plt.ylabel('P(x)')
ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)

png

#In: 
plt.figure(figsize=(18, 10))
data = df['Average per Movie'][df['Average per Movie'] < 100]
plt.xlabel('Média por Filme')
plt.ylabel('P(x)')
plt.hist(data, edgecolor='k')
ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)

png

#In: 
plt.figure(figsize=(18, 10))
data = df['Average per Movie']
plt.xlabel('Média por Filme')
plt.ylabel('P(x)')
plt.hist(data, bins=50, edgecolor='k')
ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)

png

Problemas de escala

#In: 
plt.figure(figsize=(18, 10))
mentions = [500, 505]
years = [2013, 2014]

plt.bar(years, mentions, edgecolor='k')

plt.xticks(years)
plt.ylabel("# de vezes que escutei alguém dizer 'data science'")

# define o os limites do eixo y:
plt.ylim(499,506)

plt.title("Wow! Vejam que aumento!")

ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)

png

#In: 
plt.figure(figsize=(18, 10))

plt.bar(years, mentions)

plt.xticks(years)
plt.ylabel("# de vezes que escutei alguém dizer 'data science'")

plt.ylim(0, max(mentions)*1.1)
plt.title("Humm... Até que o aumento não foi tão grande.")

ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)

png

Dados 2d com e leitura de JSON

O pandas também sabe ler dados em json! Vamos tentar.

#In: 
df = pd.read_json('capital.json')
#In: 
df.head()
estadopatrimonio_eleicao_1patrimonio_eleicao_2nome_urnacpfsigla_partidocod_unidade_eleitoral_1cod_unidade_eleitoral_2unidade_eleitoralcargo_pleiteado_1cargo_pleiteado_2ano_umano_doissequencial_candidato_1sequencial_candidato_2situacao_eleicao_1situacao_eleicao_2
0MG2326963.852890296.74Stefano Aguiar1608657PSDMGMGMinas GeraisDEPUTADO FEDERALDEPUTADO FEDERAL20142018130000001189130000613225ELEITOELEITO
1RJ4239563.823943907.61Altineu Cortes7487738PRRJRJRio De JaneiroDEPUTADO FEDERALDEPUTADO FEDERAL20142018190000001858190000604181ELEITOELEITO
2BA1077668.742281417.64Mário Negromonte Jr31226540PPBABABahiaDEPUTADO FEDERALDEPUTADO FEDERAL201420185000000016750000605225ELEITOELEITO
3CE14399524.9713160762.14Roberto Pessoa113735391PSDBCECECearáVICE-GOVERNADORDEPUTADO FEDERAL201420186000000060460000611570NÃO ELEITOELEITO
4SP713217.64972916.79Vitor Lippi168780860PSDBSPSPSão PauloDEPUTADO FEDERALDEPUTADO FEDERAL20142018250000001354250000605413ELEITOELEITO
#In: 
plt.figure(figsize=(18, 10))
plt.scatter(df['patrimonio_eleicao_1'], df['patrimonio_eleicao_2'], s=80, edgecolor='k')
linha45 = np.unique(df['patrimonio_eleicao_1'])
plt.plot(linha45, linha45)

plt.xlabel('Capital em 2014')
plt.ylabel('Capital em 2018')
ax = plt.gca()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
             ax.get_xticklabels() + ax.get_yticklabels()):
    item.set_fontsize(20)
plt.show()

png