Análise de Comics Digitais (Python) - Marvel ou DC e algumas outras conclusões
June 06, 2016
O código completo pode ser encontrado no Github, em: https://github.com/felipegalvao/comixologyscrapingand_analysis
Se preferirem, a análise também pode ser encontrada em um notebook Jupyter em: https://anaconda.org/felipegalvao/comixology-analysis-notebook-portugues/notebook ou https://github.com/felipegalvao/comixologyscrapingand_analysis/blob/master/Comixology%20Analysis%20Notebook%20-%20Portugu%C3%AAs.ipynb
Introdução
Depois de ter feito a análise do site (post aqui) e de ter feito um scraping dos dados do site da Comixology (post aqui), agora vamos fazer uma análise de dados das informações de comics digitais com Python (Pandas).
Vamos descobrir quais editoras tem os melhores preços relativos à quantidade de páginas de seus comics, as editoras com as melhores avaliações médias, além de uma análise mais profunda do duelo das gigantes: Marvel x DC Comics. Vamos começar.
Preparação inicial
Primeiro, como de costume, vamos importar os pacotes que iremos utilizar. Estes pacotes já são velhos conhecidos: numpy, pandas, matplotlib e seaborn.
Vamos também ler o arquivo csv com as informações através da função read_csv() do Pandas.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
comixology_df = pd.read_csv("comixology_comics_dataset_19.04.2016.csv",
encoding = "ISO-8859-1")
Agora, vamos criar uma coluna de preço por página. Desta forma, podemos comparar preços de uma forma mais adequada, visto que um comic com mais páginas naturalmente será mais caro. Mas, quanto mais caro? Esta coluna permitirá uma comparação mais “justa”.
Para alguns comics, a informação de quantidade de páginas não existe, e desta forma, o Pandas retorna um valor de infinito (inf) ao fazer o cálculo. Para estes, vamos definir seu valor como NaN
:
# Vamos criar uma coluna de preço por página para futuras análises
comixology_df['Price_per_page'] = pd.Series(comixology_df['Original_price'] /
comixology_df['Page Count'],
index=comixology_df.index)
# Como alguns comics estão com a contagem de páginas igual a zero, vamos
# definir para estes o Price_per_page igual a NaN
comixology_df.Price_per_page[comixology_df['Price_per_page'] == np.inf] = np.nan
Vamos agora usar a função iterrows()
do Dataframe para extrair o ano de publicação da versão impressa do comic. Esta função cria uma espécie de forloop, que itera sobre cada linha do DataFrame. Vamos usar o split para dividir a string que contém a data de publicação em uma lista. O terceiro valor será o ano. Em alguns casos, o ano retorna um valor maior que 2016. Como isto é impossível, estes valores também serão definidos como NaN
:
# Vamos extrair o ano da string de data de publicação da versão impressa
print_dates = []
for index, row in comixology_df.iterrows():
if type(comixology_df.ix[index]['Print Release Date']) == float:
row_year = np.nan
else:
row_year = int(comixology_df.ix[index]['Print Release Date'].split()[2])
if row_year > 2016:
row_year = np.nan
print_dates.append(row_year)
comixology_df['Print_Release_Year'] = pd.Series(print_dates,
index=comixology_df.index)
Para as análises (e avante)
A primeira análise que faremos será o cálculo dos valores médios do site em geral, como preço médio dos comics, quantidade média de páginas, entre outros. Usaremos a função nanmean()
do numpy, que calcula a média excluindo os NaN
, que ocorrem quando não possuímos a informação.
# Algumas informações médias do site
average_price = np.nanmean(comixology_df['Original_price'])
average_page_count = np.nanmean(comixology_df['Page Count'])
average_rating = np.nanmean(comixology_df['Rating'])
average_rating_quantity = np.nanmean(comixology_df['Ratings_Quantity'])
average_price_per_page = np.nanmean(comixology_df['Price_per_page'])
print("Preço médio: " + str(average_price))
print("Quantidade média de páginas: " + str(average_page_count))
print("Avaliação média: " + str(average_rating))
print("Quantidade média de avaliações por comic: " +
str(average_rating_quantity))
print("Preço por página médio por comic: " + str(average_price_per_page))
Preço médio: 3.69789045383
Quantidade média de páginas: 51.5862786864
Avaliação média: 4.26347617558
Quantidade média de avaliações por comic: 51.5088270335
Preço por página médio por comic: 0.0805173472536
Depois vamos listar as comics que possuem 5 estrelas, possuem mais de 20 avaliações (para captar apenas as mais representativas; comics com 5 de avaliação média mas que possuam apenas uma avaliação podem não ser uma métrica muito boa) e vamos ordena-las por preço por página. No topo teremos algumas comics que são gratuitas (as 6 primeiras) e, na visão dos usuários, de excelente qualidade. Depois, temos ótimas comics com um preço por página bem atrativo.
# Vamos listar as comics com rating 5 estrelas que possuam pelo menos
# 20 ratings e ordena-las por preço por página
comics_with_5_stars = comixology_df[comixology_df.Rating == 5]
comics_with_5_stars = comics_with_5_stars[comics_with_5_stars.Ratings_Quantity
> 20]
print(comics_with_5_stars[['Name','Publisher','Price_per_page']].
sort_values(by='Price_per_page'))
Name \
44099 Left 4 Dead: The Sacrifice
6143 Looking For Group #1
55253 The Walking Dead #1
30295 FCBD 2014: Don Rosa's Uncle Scrooge and Donald...
8762 Mother Russia #1 (of 3)
80022 Scott Pilgrim Vol. 1: Scott Pilgrim's Precious...
50270 Transformers: The Cover Collection
42749 American Elf 2001
42748 American Elf 2000
7677 Cerebus Vol. 2 #2: High Society
...
42350 Elric Vol. 1: The Ruby Throne
70993 Old Man Logan (2016-) #1
61629 Amazing Spider-Man: Renew Your Vows (2015) #2
65446 Deadpool (2012-2015) #44
72350 Spider-Man (2016-) #2
69949 Miles Morales: Ultimate Spider-Man (2014-2015) #6
67030 Giant-Size Little Marvel: AvX (2015) #4
69819 Master of Kung Fu (2015) #3 (of 4)
72166 Silver Surfer (2014-2015) #7
74037 Thanos: The Infinity Relativity
Publisher Price_per_page
44099 Valve 0.000000
6143 Blind Ferret 0.000000
55253 Image - Skybound 0.000000
30295 Fantagraphics 0.000000
8762 Alterna Comics - FUBAR Press 0.000000
80022 Oni Press 0.000000
50270 IDW 0.007674
42749 Top Shelf Productions 0.007868
42748 Top Shelf Productions 0.009967
7677 Aardvark-Vanaheim 0.011786
... ...
42350 Titan 0.199846
70993 Marvel 0.207917
61629 Marvel 0.210000
65446 Marvel 0.210000
72350 Marvel 0.210000
69949 Marvel 0.210000
67030 Marvel 0.221667
69819 Marvel 0.221667
72166 Marvel 0.234706
74037 Marvel 0.249900
Na próxima análise, usaremos apenas comics com mais de 5 avaliações. Para isso, vamos filtrar o DataFrame. Vamos então criar uma pivot table do Pandas, para visualizarmos a quantidade de comics com avaliação e a avaliação média deste Publisher. Depois vamos considerar como Publishers representativas aquelas que possuem pelo menos 20 comics com avaliações. Para isso faremos o filtro da pivot table. Depois, vamos ordenar esta tabela filtrada por avaliação média, em ordem decrescente. Ou seja, as primeiras Publishers são consideradas as que possuem a melhor avaliação média de seus comics.
# Para a próxima análise, usaremos somente comics com mais de 5 ratings
comics_more_than_5_ratings = comixology_df[comixology_df.Ratings_Quantity > 5]
# Criar pivot table com média das avaliações por Publisher
publishers_avg_rating = pd.pivot_table(comics_more_than_5_ratings,
values=['Rating'],
index=['Publisher'],
aggfunc=[np.mean, np.count_nonzero])
# Primeiramente vamos avaliar qualquer publisher que tenha mais de 20 comics
# com avaliações
main_pub_avg_rating = publishers_avg_rating[publishers_avg_rating.
count_nonzero.Rating > 20]
main_pub_avg_rating = main_pub_avg_rating.sort_values(by=('mean','Rating'),
ascending=False)
print(main_pub_avg_rating)
mean count_nonzero
Rating Rating
Publisher
Cartoon Books 4.875000 80
Aardvark-Vanaheim 4.800000 25
Abstract Studio 4.786517 89
BOOM! - BOOM! Box 4.763889 72
Archie 4.711656 326
Icon 4.585859 198
Evil Twin Comics 4.565217 23
Udon 4.561798 89
MAX 4.546911 437
Fantagraphics 4.518182 110
... ...
Asylum Press 3.898305 59
A Wave Blue World 3.809524 21
AAM-Markosia 3.787097 155
Alternative Comics 3.764706 34
Kickstart 3.695652 23
Arcana Comics 3.650794 315
DMP 3.636364 99
Moonstone 3.531915 47
Harlequin/ SB Creative Corp. 3.525641 78
Stormfront Comics 3.333333 78
[70 rows x 2 columns]
Repare que as grandes Marvel e DC Comics não estão entre as primeiras. Se vermos a tabela por completo, elas na verdade estão do meio para baixo.
Para ajudar na visão, um gráfico em matplotlib que representa a tabela acima:
# Agora, um gráfico com a avaliação média de cada editora
y_axis = main_pub_avg_rating['mean']['Rating']
x_axis = range(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis, tuple(main_pub_avg_rating.index),rotation=90)
plt.show()
Para simplificar um pouco e ter uma tabela e gráfico mais fáceis de visualizar, vamos considerar agora comics que possuem pelo menos 300 comics com avaliações. Primeiro, a tabela:
# E agora vamos ver as bem grandes, com mais de 300 comics com avaliações
big_pub_avg_rating = publishers_avg_rating[publishers_avg_rating.
count_nonzero.Rating > 300]
big_pub_avg_rating = big_pub_avg_rating.sort_values(by=('mean','Rating'),
ascending=False)
print(big_pub_avg_rating)
mean count_nonzero
Rating Rating
Publisher
Archie 4.711656 326
MAX 4.546911 437
Image - Skybound 4.504092 611
Dark Horse 4.440000 550
Vertigo 4.435793 2453
Image 4.316908 3105
IDW 4.313492 2772
Zenescope 4.309711 381
Oni Press 4.305376 465
Valiant 4.291022 323
BOOM! Studios 4.277219 1194
Avatar 4.234672 473
DC Comics 4.218644 13012
Image - Top Cow 4.176545 793
Image - Todd McFarlane Productions 4.171429 350
Marvel 4.154245 11177
Dynamite 4.110597 1944
Arcana Comics 3.650794 315
E agora o gráfico que representa a tabela (menos poluído e permitindo uma visão melhor da situação das publishers):
# E agora, o mesmo gráfico com a avaliação média das grandes editoras
y_axis = big_pub_avg_rating['mean']['Rating']
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.5, tuple(big_pub_avg_rating.index), rotation=90)
plt.show()
Uma coisa que eu acreditava que fosse legal de conferir era se a classificação etária faz alguma diferença na avaliação que os usuários dão a cada comic. Será que comics voltados para o público adulto possuem avaliações melhores? Ou ao contrário? Vamos checar fazendo uma nova pivot table:
# Vamos ver agora se a classificação etária faz alguma diferença significativa
# nas avaliações
rating_by_age = pd.pivot_table(comics_more_than_5_ratings,
values=['Rating'],
index=['Age Rating'],
aggfunc=[np.mean, np.count_nonzero])
print(rating_by_age)
mean count_nonzero
Rating Rating
Age Rating
12+ Only 4.185380 28304
15+ Only 4.218854 4487
17+ Only 4.341259 9925
18+ Only 4.143939 264
9+ Only 4.360186 1502
All Ages 4.395935 1230
E abaixo o gráfico que representa a tabela acima:
# Gráfico de barras com a avaliação média por faixa etária
y_axis = rating_by_age['mean']['Rating']
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.25, tuple(rating_by_age.index), rotation=45)
plt.show()
Como podemos perceber, as barras do gráfico ficam com alturas bem próximas. Ao que parece, a classificação etária não afeta muito significativamente a avaliação dos comics. Se formos analisar apenas matematicamente, as comics liberadas para qualquer idade ou para maiores de 9 anos possuem as melhores avaliações. Mas não é possível enxergar uma relação clara.
Nosso próximo passo é ver como, de certa forma, evoluiu o número de lançamentos de quadrinhos (considerando as versões impressas) ao longo dos anos. Lembrando que já criamos a coluna com o ano de lançamento da versão impressa de um comic. O próximo passo é basicamente contar a quantidade de cada ano nesta coluna que criamos. Vamos fazer uma lista com os anos de lançamento passados para inteiros para que o eixo do gráfico faça a leitura correta:
# Cria tabela com a quantidade de quadrinhos lançados por ano, baseado na data
# de lançamento da versão impressa
print_releases_per_year = pd.pivot_table(comixology_df,
values=['Name'],
index=['Print_Release_Year'],
aggfunc=[np.count_nonzero])
print_years = []
for index, row in print_releases_per_year.iterrows():
print_year = int(index)
print_years.append(print_year)
print_releases_per_year.index = print_years
print(print_releases_per_year)
count_nonzero
Name
1900 14
1938 1
1939 6
1940 7
1941 21
1942 22
1943 18
1944 13
1945 13
1946 15
...
2007 2165
2008 2664
2009 2920
2010 3500
2011 4217
2012 4501
2013 3446
2014 5369
2015 4413
2016 2005
[80 rows x 1 columns]
E agora vamos construir um gráfico de linha para visualizar a situação melhor:
y_axis = print_releases_per_year['count_nonzero']['Name']
x_axis = print_releases_per_year['count_nonzero']['Name'].index
plt.figure(figsize=(10, 6))
plt.plot(x_axis, y_axis)
plt.show()
Os números mostram que o crescimento era moderado, até que na década de 2000 ocorreu um boom, com um crescimento bastante considerável até 2012, quando a quantidade de lançamentos começou a oscilar. A queda mostrada no ano de 2016 ocorre, obviamente, pois ainda estamos na metade do mesmo.
Agora, partimos para fazer uma avaliação dos comics mais baixados no site (não dá para dizer que são os mais comprados, visto que alguns dos comics são gratuitos e a informação de vendas de um determinado comic não estão disponíveis). Para esta análise, vamos ver quais são os 30 comics com mais avaliações.
# Vejamos agora as 30 comics com mais avaliações; se for mantida a proporção,
# pode-se dizer que estas são as comics mais baixadas (e não vendidas, pois
# algumas destas são gratuitas), mas isto também não é garantido
comics_by_ratings_quantity = comixology_df[['Name','Publisher',
'Ratings_Quantity']].sort_values(
by='Ratings_Quantity',
ascending=False)
print(comics_by_ratings_quantity.head(30))
Name Publisher \
55253 The Walking Dead #1 Image - Skybound
41479 Arrow (2012-2013) #1: Special Edition DC Comics
53325 Saga #1 Image
11898 Bane 101 DC Comics
16638 Injustice: Gods Among Us (2013) #1 DC Comics
12709 Batman (2011-) #1 DC Comics
17286 Justice League (2011-) #1 DC Comics
55228 The Walking Dead Vol. 1: Days Gone Bye Image - Skybound
19435 Batman: Night of the Owls Booklet #1 DC Comics
549 Batman Black & White: A Black and White World DC Comics
231 Batman 101 DC Comics
2246 Blackest Night #0 DC Comics
61309 Amazing Spider-Man (1999-2013) #36 Marvel
49490 Teenage Mutant Ninja Turtles #1 IDW
933 Batman: Gotham Adventures #1 DC Comics
22805 Superman: War of the Supermen #0 (of 0) DC Comics
13226 Death of the Family: Preview DC Comics
12722 Batman (2011-) #13 DC Comics
64194 Captain America: The First Avenger #1: First V... Marvel
62884 Avengers: Heroes Welcome #1 Marvel
80024 Scott Pilgrim Free Comic Book Day Story Oni Press
69771 Marvel's Jessica Jones #1 Marvel
49753 Transformers: All Hail Megatron #1 IDW
553 Batman Black & White: Two of A Kind DC Comics
51029 Chew #1 Image
13749 Detective Comics (2011-) #1 DC Comics
51699 Girls #1 Image
54956 Invincible #1 Image - Skybound
40223 52 Week #1 DC Comics
232 Batman 201 DC Comics
Ratings_Quantity
55253 38841
41479 8885
53325 7747
11898 6749
16638 6608
12709 6305
17286 5483
55228 5408
19435 5091
549 4920
231 4710
2246 4644
61309 4436
49490 4408
933 4335
22805 4301
13226 3960
12722 3753
64194 3672
62884 3660
80024 3652
69771 3633
49753 3625
553 3489
51029 3434
13749 3303
51699 3279
54956 3275
40223 3248
232 3162
E o gráfico das comics com mais avaliações:
y_axis = comics_by_ratings_quantity.head(30)['Ratings_Quantity']
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.5, tuple(comics_by_ratings_quantity.head(30)['Name']),
rotation=90)
plt.show()
Walking Dead liderando com (muuuita) folga. Depois, algumas comics de Marvel e DC e mais alguns comics variados.
Agora, vamos fazer uma análise mais profunda dos comics das gigantes: Marvel e DC Comics.
Duelo de Gigantes: Marvel vs DC Comics
Primeiro, vamos filtrar o DataFrame para que sobrem apenas comics destas duas. Depois, vamos calcular através da função pivot table alguns valores médios das duas:
# Vamos agora ver dados somente das duas maiores: Marvel e DC
marvel_dc_comics = comixology_df[(comixology_df.Publisher == 'Marvel') |
(comixology_df.Publisher == 'DC Comics')]
# Primeiro, alguns valores médios de cada uma
marvel_dc_pivot_averages = pd.pivot_table(marvel_dc_comics,
values=['Rating','Original_price','Page Count',
'Price_per_page'],
index=['Publisher'],
aggfunc=[np.mean])
print(marvel_dc_pivot_averages)
mean
Original_price Page Count Price_per_page Rating
Publisher
DC Comics 2.600034 35.318463 0.078356 4.233034
Marvel 3.398555 41.344295 0.090946 4.191335
Como vemos, a DC possui um preço médio e preço por página menor, enquanto possui uma avaliação média levemente maior. A quantidade média de páginas nos comics da Marvel é um pouco maior. Abaixo, os gráficos de barra representando cada uma destas comparações:
plt.figure(1,figsize=(10, 6))
plt.subplot(221) # Mean original price
y_axis = marvel_dc_pivot_averages['mean']['Original_price']
x_axis = np.arange(len(marvel_dc_pivot_averages['mean']['Original_price']))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4,
tuple(marvel_dc_pivot_averages['mean']['Original_price'].index))
plt.title('Mean Original Price')
plt.tight_layout()
plt.subplot(222) # Mean page count
y_axis = marvel_dc_pivot_averages['mean']['Page Count']
x_axis = np.arange(len(marvel_dc_pivot_averages['mean']['Page Count']))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4,
tuple(marvel_dc_pivot_averages['mean']['Page Count'].index))
plt.title('Mean Page Count')
plt.tight_layout()
plt.subplot(223) # Mean Price Per Page
y_axis = marvel_dc_pivot_averages['mean']['Price_per_page']
x_axis = np.arange(len(marvel_dc_pivot_averages['mean']['Price_per_page']))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4,
tuple(marvel_dc_pivot_averages['mean']['Price_per_page'].index))
plt.title('Mean Price Per Page')
plt.tight_layout()
plt.subplot(224) # Mean Comic Rating
y_axis = marvel_dc_pivot_averages['mean']['Rating']
x_axis = np.arange(len(marvel_dc_pivot_averages['mean']['Rating']))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4,
tuple(marvel_dc_pivot_averages['mean']['Rating'].index))
plt.title('Mean Comic Rating')
plt.tight_layout()
plt.show()
O próximo passo é ver alguns números relativos a quantidade de comics de cada uma. Quantos comics cada uma possui, qual o número de comics bons (avaliação 4 ou 5) e ruins (avaliação 1 ou 2) e sua proporção perante o número total de comics. Para fazer esta análise, vamos meramente fazer alguns filtros e verificar o comprimento do DataFrame após estes filtros. Simples:
# Vamos agora verificar a quantidade de comics de cada uma e fazer uma proporção
# com a quantidade de comics de cada uma com rating maior ou igual a 4. Desta
# forma podemos ver qual delas, proporcionalmente, lança bons comics
marvel_total = len(marvel_dc_comics[marvel_dc_comics['Publisher'] == 'Marvel'])
marvel_4_or_5 = len(marvel_dc_comics[(marvel_dc_comics['Publisher'] == 'Marvel')
& (marvel_dc_comics['Rating'] >= 4)])
marvel_proportion_4_or_5 = marvel_4_or_5 / marvel_total
marvel_1_or_2 = len(marvel_dc_comics[(marvel_dc_comics['Publisher'] == 'Marvel')
& (marvel_dc_comics['Rating'] <= 2)])
marvel_proportion_1_or_2 = marvel_1_or_2 / marvel_total
dc_total = len(marvel_dc_comics[marvel_dc_comics['Publisher'] == 'DC Comics'])
dc_4_or_5 = len(marvel_dc_comics[(marvel_dc_comics['Publisher'] == 'DC Comics')
& (marvel_dc_comics['Rating'] >= 4)])
dc_proportion_4_or_5 = dc_4_or_5 / dc_total
dc_1_or_2 = len(marvel_dc_comics[(marvel_dc_comics['Publisher'] == 'DC Comics')
& (marvel_dc_comics['Rating'] <= 2)])
dc_proportion_1_or_2 = dc_1_or_2 / dc_total
print("\n")
print("Total de Comics Marvel: " + str(marvel_total))
print("Total de Comics Marvel com avaliação maior ou igual a 4: " +
str(marvel_4_or_5))
print("Proporção de Comics Marvel com avaliação maior ou igual a 4: " +
str("{0:.2f}%".format(marvel_proportion_4_or_5 * 100)))
print("Total de Comics Marvel com avaliação menor ou igual a 2: " +
str(marvel_1_or_2))
print("Proporção de Comics Marvel com avaliação menor ou igual a 2: " +
str("{0:.2f}%".format(marvel_proportion_1_or_2 * 100)))
print("\n")
print("Total de Comics DC Comics: " + str(dc_total))
print("Total de Comics DC Comics com avaliação maior ou igual a 4: " +
str(dc_4_or_5))
print("Proporção de Comics DC Comics com avaliação maior ou igual a 4: " +
str("{0:.2f}%".format(dc_proportion_4_or_5 * 100)))
print("Total de Comics DC Comics com avaliação menor ou igual a 2: " +
str(dc_1_or_2))
print("Proporção de Comics DC Comics com avaliação menor ou igual a 2: " +
str("{0:.2f}%".format(dc_proportion_1_or_2 * 100)))
print("\n")
Total de Comics Marvel: 18063
Total de Comics Marvel com avaliação maior ou igual a 4: 14791
Proporção de Comics Marvel com avaliação maior ou igual a 4: 81.89%
Total de Comics Marvel com avaliação menor ou igual a 2: 95
Proporção de Comics Marvel com avaliação menor ou igual a 2: 0.53%
Total de Comics DC Comics: 17440
Total de Comics DC Comics com avaliação maior ou igual a 4: 15986
Proporção de Comics DC Comics com avaliação maior ou igual a 4: 91.66%
Total de Comics DC Comics com avaliação menor ou igual a 2: 62
Proporção de Comics DC Comics com avaliação menor ou igual a 2: 0.36%
Novamente, aqui, a DC Comics se mostra um pouquinho melhor. Tem uma maior proporção de comics bons e uma menor proporção de comics ruins. Ponto para a DC de novo. Abaixo, o gráfico fazendo as comparações:
plt.figure(2,figsize=(10, 6))
plt.subplot(221) # Total de Comics de cada editora
y_axis = [dc_total, marvel_total]
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4, ('DC Comics','Marvel'))
plt.title('Comics Totais')
plt.tight_layout()
plt.subplot(222) # Proporção de Comics com avaliação 4 ou 5
y_axis = [dc_proportion_4_or_5 * 100, marvel_proportion_4_or_5 * 100]
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4, ('DC Comics','Marvel'))
plt.title('Proporção de Comics com avaliação 4 ou 5')
plt.tight_layout()
plt.subplot(223) # Proporção de Comics com avaliação 1 ou 2
y_axis = [dc_proportion_1_or_2 * 100, marvel_proportion_1_or_2 * 100]
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4, ('DC Comics','Marvel'))
plt.title('Proporção de Comics com avaliação 1 ou 2')
plt.tight_layout()
plt.show()
Apenas como curiosidade, vamos verificar o número de avaliações dadas em comics de cada uma, através de mais uma pivot table:
# Somar a quantidade de avaliações em comics de cada editora
marvel_dc_pivot_sums = pd.pivot_table(marvel_dc_comics,
values=['Ratings_Quantity'],
index=['Publisher'],
aggfunc=[np.sum])
print(marvel_dc_pivot_sums)
sum
Ratings_Quantity
Publisher
DC Comics 1725344
Marvel 1099324
Interessante notar que mesmo a Marvel tendo uma quantidade maior de comics, como vimos na tabela anterior, a quantidade de avaliações em comics da DC é bem maior, cerca de 55% a mais. Parece que os fãs dos comics da DC são mais propensos a avaliar os comics na Comixology que os da Marvel.
Nossa próxima avaliação será a de personagens e equipes de heróis / vilões. Primeiramente, vamos criar listas com personagens de cada uma, e igualmente para times. Dividi os personagens entre as Publishers e criei as listas na mão. Deu um trabalhinho, mas nada demais.
main_dc_characters = ['Superman','Batman','Aquaman','Wonder Woman', 'Flash',
'Robin','Arrow', 'Batgirl', 'Bane', 'Harley Queen',
'Poison Ivy', 'Joker','Firestorm','Vixen',
'Martian Manhunter','Zod','Penguin','Lex Luthor',
'Green Lantern','Supergirl','Atom','Cyborg','Hawkgirl',
'Starfire','Jonah Hex','Booster Gold','Black Canary',
'Shazam','Catwoman','Nightwing','Zatanna','Hawkman',
'Power Girl','Rorschach','Doctor Manhattan',
'Blue Beetle','Batwoman','Darkseid','Vandal Savage',
"Ra's Al Ghul",'Riddler','Reverse Flash','Black Adam',
'Deathstroke','Brainiac','Sinestro','Two-Face']
main_marvel_characters = ['Spider-Man','Captain Marvel','Hulk','Thor',
'Iron Man','Luke Cage','Black Widow','Daredevil',
'Captain America','Jessica Jones','Ghost Rider',
'Spider-Woman','Silver Surfer','Beast','Thing',
'Kitty Pride','Doctor Strange','Black Panther',
'Invisible Woman','Nick Fury','Storm','Professor X',
'Cyclops','Jean Grey','Wolverine','Scarlet Witch',
'Gambit','Rogue','X-23','Iceman','She-Hulk',
'Iron Fist','Hawkeye','Quicksilver','Vision',
'Ant-Man','Cable','Bishop','Colossus','Deadpool',
'Human Torch','Mr. Fantastic','Nightcrawler','Nova',
'Psylocke','Punisher','Rocket Raccoon','Groot',
'Star-Lord','War Machine','Gamora','Drax','Venom',
'Carnage','Octopus','Green Goblin','Abomination',
'Enchantress','Sentinel','Viper','Lady Deathstrike',
'Annihilus','Ultron','Galactus','Kang','Bullseye',
'Juggernaut','Sabretooth','Mystique','Kingpin',
'Apocalypse','Thanos','Dark Phoenix','Loki',
'Red Skull','Magneto','Doctor Doom','Ronan']
dc_teams = ['Justice League','Teen Titans','Justice Society','Lantern Corps',
'Legion of Super-Heroes','All-Star Squadron','Suicide Squad',
'Birds of Prey','Gen13', 'The League of Extraordinary Gentlemen',
'Watchmen']
marvel_teams = ['X-Men','Avengers','Fantastic Four','Asgardian Gods','Skrulls',
'S.H.I.E.L.D.','Inhumans','A.I.M.','X-Factor','X-Force',
'Defenders','New Mutants','Brotherhood of Evil Mutants',
'Thunderbolts', 'Alpha Flight','Guardians of the Galaxy',
'Nova Corps','Illuminati']
Agora, vamos passar por cada nome de personagem e time. Primeiramente, vamos definir um DataFrame, e faremos um filtro nos nomes das comics que possuem o nome deste personagem ou time. Depois, vamos extrair algumas informações daí. A quantidade de comics será basicamente o número de linhas do DataFrame, obtido através da função len()
. Depois, as médias de avaliação, preço e quantidade de páginas. Cada uma destas informações será salva em um dictionary, que será adicionado a uma lista. No fim, teremos uma lista de dicts para os personagens e uma lista de dicts para os times, e a partir daí criaremos os DataFrames de cada um:
character_row = {}
characters_dicts = []
for character in main_dc_characters:
character_df = comixology_df[(comixology_df['Name'].str.contains(character)) &
(comixology_df['Publisher'] == 'DC Comics')]
character_row['Character_Name'] = character
character_row['Quantity_of_comics'] = len(character_df)
character_row['Average_Rating'] = np.nanmean(character_df['Rating'])
character_row['Average_Price'] = np.nanmean(character_df['Original_price'])
character_row['Average_Pages'] = np.nanmean(character_df['Page Count'])
character_row['Publisher'] = "DC Comics"
characters_dicts.append(character_row)
character_row = {}
for character in main_marvel_characters:
character_df = comixology_df[(comixology_df['Name'].str.contains(character)) &
(comixology_df['Publisher'] == 'Marvel')]
character_row['Character_Name'] = character
character_row['Quantity_of_comics'] = len(character_df)
character_row['Average_Rating'] = np.nanmean(character_df['Rating'])
character_row['Average_Price'] = np.nanmean(character_df['Original_price'])
character_row['Average_Pages'] = np.nanmean(character_df['Page Count'])
character_row['Publisher'] = "Marvel"
characters_dicts.append(character_row)
character_row = {}
characters_df = pd.DataFrame(characters_dicts)
team_row = {}
teams_dicts = []
for team in dc_teams:
team_df = comixology_df[(comixology_df['Name'].str.contains(team)) &
(comixology_df['Publisher'] == 'DC Comics')]
team_row['Team_Name'] = team
team_row['Quantity_of_comics'] = len(team_df)
team_row['Average_Rating'] = np.nanmean(team_df['Rating'])
team_row['Average_Price'] = np.nanmean(team_df['Original_price'])
team_row['Average_Pages'] = np.nanmean(team_df['Page Count'])
team_row['Publisher'] = "DC Comics"
teams_dicts.append(team_row)
team_row = {}
for team in marvel_teams:
team_df = comixology_df[(comixology_df['Name'].str.contains(team)) &
(comixology_df['Publisher'] == 'Marvel')]
team_row['Team_Name'] = team
team_row['Quantity_of_comics'] = len(team_df)
team_row['Average_Rating'] = np.nanmean(team_df['Rating'])
team_row['Average_Price'] = np.nanmean(team_df['Original_price'])
team_row['Average_Pages'] = np.nanmean(team_df['Page Count'])
team_row['Publisher'] = "Marvel"
teams_dicts.append(team_row)
team_row = {}
teams_df = pd.DataFrame(teams_dicts)
Vamos considerar apenas times e personagens que possuam mais de 20 comics onde seu nome está no título do comic.
teams_df = teams_df[teams_df['Quantity_of_comics'] > 20]
characters_df = characters_df[characters_df['Quantity_of_comics'] > 20]
Vamos agora verificar os maiores times e personagens em número de comics e avaliação média. Para os personagens, mesmo considerando aqueles com mais de 20 comics, ainda sobra muita coisa. Desta forma, vamos limitar a quantidade de personagens a 20, para que a lista e o gráfico não fiquem muito extensos. Depois vamos imprimir cada uma das tabelas.
# Limitando a 20 o número de personagens
top_characters_by_quantity = characters_df.sort_values(by='Quantity_of_comics',
ascending=False)[['Character_Name',
'Average_Rating',
'Quantity_of_comics']].head(20)
top_characters_by_rating = characters_df.sort_values(by='Average_Rating',
ascending=False)[['Character_Name',
'Average_Rating',
'Quantity_of_comics']].head(20)
top_teams_by_quantity = teams_df.sort_values(by='Quantity_of_comics',
ascending=False)[['Team_Name',
'Average_Rating',
'Quantity_of_comics']]
top_teams_by_rating = teams_df.sort_values(by='Average_Rating',
ascending=False)[['Team_Name',
'Average_Rating',
'Quantity_of_comics']]
print(top_characters_by_quantity)
Character_Name Average_Rating Quantity_of_comics
1 Batman 4.218568 2459
47 Spider-Man 4.335099 1680
0 Superman 4.197286 1043
55 Captain America 3.949602 831
51 Iron Man 4.083821 744
49 Hulk 4.098540 707
18 Green Lantern 4.132159 694
71 Wolverine 4.122517 631
4 Flash 4.206271 616
3 Wonder Woman 4.313629 615
50 Thor 4.251244 597
54 Daredevil 4.306867 529
86 Deadpool 4.319018 504
5 Robin 4.308235 429
6 Arrow 4.223214 341
19 Supergirl 4.205036 296
28 Catwoman 3.920635 266
2 Aquaman 4.153543 261
29 Nightwing 4.248869 221
7 Batgirl 4.307692 195
Entre os personagens, temos o Batman com o maior número de comics, seguido pelo Homem-Aranha e, bem atrás o Superman completando o top 3. Depois temos uma série de outros heróis famosos, como Capitão América, Homem de Ferro, Wolverine, Flash, entre outros. Aqui, nada de muito surpreendente.
print(top_characters_by_rating)
Character_Name Average_Rating Quantity_of_comics
115 Mystique 4.666667 27
25 Booster Gold 4.633803 83
24 Jonah Hex 4.632911 84
14 Martian Manhunter 4.611111 55
35 Blue Beetle 4.542373 59
59 Silver Surfer 4.468750 82
64 Black Panther 4.418033 150
52 Luke Cage 4.388889 29
83 Cable 4.361111 144
81 Vision 4.352941 56
92 Punisher 4.351852 164
78 Iron Fist 4.348624 114
96 War Machine 4.347826 29
69 Cyclops 4.346154 27
20 Atom 4.336735 119
47 Spider-Man 4.335099 1680
75 X-23 4.333333 39
122 Magneto 4.324324 37
86 Deadpool 4.319018 504
3 Wonder Woman 4.313629 615
Aqui temos uma surpresa na liderança. Mesmo com a quantidade de comics não sendo tão grande, acho difícil que alguém previsse que a Mystique seria o personagem com a avaliação média mais alta no meio destes personagens todos, tão mais populares. Nas primeiras posições, outros resultados surpreendentes, com Booster Gold em segundo, Jonah Hex em terceiro, Blue Beetle em quinto. Dos super populares que vimos na lista de cima, temos o Spider-Man, Deadpool e Wonder Woman, já no fim da lista do top 20. Agora, aos times:
print(top_teams_by_quantity)
Team_Name Average_Rating Quantity_of_comics
11 X-Men 4.117677 2025
12 Avengers 4.063710 1721
0 Justice League 4.190608 744
13 Fantastic Four 4.469671 632
1 Teen Titans 4.341518 457
4 Legion of Super-Heroes 4.268966 326
19 X-Factor 4.253521 224
7 Birds of Prey 4.167513 198
20 X-Force 4.240838 193
6 Suicide Squad 4.006329 159
26 Guardians of the Galaxy 4.132812 143
3 Lantern Corps 4.125926 136
22 New Mutants 4.095238 130
24 Thunderbolts 4.431193 127
16 S.H.I.E.L.D. 4.053763 123
21 Defenders 4.066667 87
2 Justice Society 4.142857 77
17 Inhumans 4.245902 69
10 Watchmen 4.345455 55
5 All-Star Squadron 4.500000 50
8 Gen13 4.175000 40
Entre os times com mais comics, nada de muito surpreendente também. Os eternos X-Men em primeiro, Avengers em segundo e Justice League em terceiro. Depois, seguem os outros times menos populares.
print(top_teams_by_rating)
Team_Name Average_Rating Quantity_of_comics
5 All-Star Squadron 4.500000 50
13 Fantastic Four 4.469671 632
24 Thunderbolts 4.431193 127
10 Watchmen 4.345455 55
1 Teen Titans 4.341518 457
4 Legion of Super-Heroes 4.268966 326
19 X-Factor 4.253521 224
17 Inhumans 4.245902 69
20 X-Force 4.240838 193
0 Justice League 4.190608 744
8 Gen13 4.175000 40
7 Birds of Prey 4.167513 198
2 Justice Society 4.142857 77
26 Guardians of the Galaxy 4.132812 143
3 Lantern Corps 4.125926 136
11 X-Men 4.117677 2025
22 New Mutants 4.095238 130
21 Defenders 4.066667 87
12 Avengers 4.063710 1721
16 S.H.I.E.L.D. 4.053763 123
6 Suicide Squad 4.006329 159
Nas avaliações, o top 3 é formado pelo All-Star Squadron, da DC, Fantastic Four e Thunderbolts, da Marvel. Surpreendentemente, X-Men, Avengers e o Suicide Squad (cujo filme está chegando em breve), ficam na parte de baixo da lista.
Abaixo plotamos estes gráficos para ajudar na visualização.
plt.figure(3,figsize=(10, 6))
plt.subplot(121) # Personagem por quantidade de comics
y_axis = top_characters_by_quantity['Quantity_of_comics']
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4, tuple(top_characters_by_quantity['Character_Name']),
rotation=90)
plt.title('Personagem por Qtd de Comics')
plt.tight_layout()
plt.subplot(122) # Personagem por avaliação média
y_axis = top_characters_by_rating['Average_Rating']
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4, tuple(top_characters_by_rating['Character_Name']),
rotation=90)
plt.title('Personagem por Avaliação Média')
plt.tight_layout()
plt.show()
E agora os gráficos dos times:
plt.figure(4,figsize=(10, 6))
plt.subplot(121) # Time por quantidade de comics
y_axis = top_teams_by_quantity['Quantity_of_comics']
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4, tuple(top_teams_by_quantity['Team_Name']), rotation=90)
plt.title('Time por quantidade de comics')
plt.tight_layout()
plt.subplot(122) # Time por avaliação média
y_axis = top_teams_by_rating['Average_Rating']
x_axis = np.arange(len(y_axis))
plt.bar(x_axis, y_axis)
plt.xticks(x_axis+0.4, tuple(top_teams_by_rating['Team_Name']), rotation=90)
plt.title('Time por avaliação média')
plt.tight_layout()
plt.show()
Conclusão
Com isto, encerra-se a nossa série de 3 posts sobre análise do site, web scraping e análise de dados de comics digitais, com informações extraídas do site da Comixology. Como nem sempre os dados estão disponíveis de uma forma simples e prática, como um banco de dados ou um dataset em csv, podemos ter que buscar os dados através de web scraping, varrendo links em busca das informações que queremos.
Aqui nesta análise, chegamos a conclusões relativas aos comics presentes no site, na visão de seus usuários. Um resumo das conclusões está na lista abaixo:
- Alguns publishers menores possuem avaliações médias altas, sendo uma boa opção de leitura se você quer fugir das tradicionais Marvel e DC Comics
- Entre as gigantes (publishers com mais de 300 comics que receberam alguma avaliação no site), a Marvel e a DC Comics ficam na parte debaixo da lista com relação à avaliações médias. As três primeiras são Archie (do comic de mesmo nome, Mega Man, Sonic, entre outros), MAX (focada em comics mais adultos: Dexter, Jessica Jones, Deadpool) e Image - Skybound (principalmente Walking Dead).
- Classificação Etária não parece fazer muita diferença na avaliação que um comic recebe.
- O lançamento de comics aumentou muito na década de 2000, sofreu uma queda recentemente e agora parece oscilar ao longo dos anos.
- Os dois comics com mais avaliações no site são gratuitos. O terceiro, surpreendentemente, é a primeira edição da série Saga, da publisher Image.
- Na batalha particular entre Marvel e DC Comics, a DC parece levar pequena vantagem. Ela possui um preço médio e preço por página menor, enquanto possui uma avaliação média levemente maior. A quantidade média de páginas nos comics da Marvel é um pouco maior. A DC também possui uma proporção maior de comics bons (4 ou 5 estrelas) e uma proporção menor de comics ruins (1 ou 2 estrelas).
- O Batman é o personagem com mais comics, seguido do Homem-Aranha e Superman. Os heróis com maior avaliação média são, surpreendentemente, Mística (dos X-Men), Booster Gold e Jonah Hex.
- Entre os times, os que mais possuem comics são os X-Men, Vingadores e a Liga da Justiça. No pódio dos mais bem avaliados estão o All-Star Squadron da DC, Quarteto Fantástico e os Thunderbolts, da Marvel.
E assim terminamos nosso pequeno projeto. Espero que tenham gostado :)
Abraços