Introdução
Neste laboratório, aprenderemos como criar um gráfico de bolhas empacotadas (packed-bubble chart) usando Matplotlib em Python. O gráfico de bolhas empacotadas é um tipo de gráfico que exibe dados em bolhas de tamanhos diferentes, com o tamanho da bolha representando a magnitude dos dados. Este gráfico é útil para exibir dados escalares, onde os dados são um único valor numérico associado a um item.
Dicas da VM
Após a inicialização da VM, clique no canto superior esquerdo para mudar para a aba Notebook para acessar o Jupyter Notebook para praticar.
Às vezes, pode ser necessário aguardar alguns segundos para que o Jupyter Notebook termine de carregar. A validação das operações não pode ser automatizada devido a limitações no Jupyter Notebook.
Se você enfrentar problemas durante o aprendizado, sinta-se à vontade para perguntar ao Labby. Forneça feedback após a sessão, e resolveremos o problema prontamente para você.
Importar as Bibliotecas Necessárias
Para criar um gráfico de bolhas empacotadas, precisamos importar as bibliotecas matplotlib.pyplot e numpy. A biblioteca numpy é usada para realizar operações matemáticas em arrays, o que é útil para calcular os tamanhos das bolhas.
import matplotlib.pyplot as plt
import numpy as np
Definir os Dados
Os dados que usaremos para este exemplo são a participação de mercado de diferentes navegadores de desktop. Definiremos os dados como um dicionário contendo os nomes dos navegadores, a participação de mercado e a cor para cada bolha.
browser_market_share = {
'browsers': ['firefox', 'chrome', 'safari', 'edge', 'ie', 'opera'],
'market_share': [8.61, 69.55, 8.36, 4.12, 2.76, 2.43],
'color': ['#5A69AF', '#579E65', '#F9C784', '#FC944A', '#F24C00', '#00B825']
}
Definir a Classe BubbleChart
A classe BubbleChart é usada para criar o gráfico de bolhas empacotadas. A classe recebe um array de áreas de bolhas e um valor de espaçamento entre as bolhas. O método __init__ configura as posições iniciais das bolhas e calcula a distância máxima de passo (maximum step distance), que é a distância que cada bolha pode se mover em uma única iteração.
class BubbleChart:
def __init__(self, area, bubble_spacing=0):
"""
Configuração para o colapso das bolhas.
Parâmetros
----------
area : array-like
Área das bolhas.
bubble_spacing : float, default: 0
Espaçamento mínimo entre as bolhas após o colapso.
Notas
-----
Se "area" estiver ordenado, os resultados podem parecer estranhos.
"""
area = np.asarray(area)
r = np.sqrt(area / np.pi)
self.bubble_spacing = bubble_spacing
self.bubbles = np.ones((len(area), 4))
self.bubbles[:, 2] = r
self.bubbles[:, 3] = area
self.maxstep = 2 * self.bubbles[:, 2].max() + self.bubble_spacing
self.step_dist = self.maxstep / 2
## calculate initial grid layout for bubbles
length = np.ceil(np.sqrt(len(self.bubbles)))
grid = np.arange(length) * self.maxstep
gx, gy = np.meshgrid(grid, grid)
self.bubbles[:, 0] = gx.flatten()[:len(self.bubbles)]
self.bubbles[:, 1] = gy.flatten()[:len(self.bubbles)]
self.com = self.center_of_mass()
Definir Métodos de Movimento das Bolhas
A classe BubbleChart também contém métodos para mover as bolhas em direção ao centro de massa e verificar colisões com outras bolhas. O método center_of_mass calcula o centro de massa de todas as bolhas, e o método center_distance calcula a distância entre uma bolha e o centro de massa. O método outline_distance calcula a distância entre o contorno de uma bolha e os contornos de outras bolhas, e o método check_collisions verifica se uma nova posição de bolha colide com outras bolhas.
def center_of_mass(self):
return np.average(
self.bubbles[:, :2], axis=0, weights=self.bubbles[:, 3]
)
def center_distance(self, bubble, bubbles):
return np.hypot(bubble[0] - bubbles[:, 0],
bubble[1] - bubbles[:, 1])
def outline_distance(self, bubble, bubbles):
center_distance = self.center_distance(bubble, bubbles)
return center_distance - bubble[2] - \
bubbles[:, 2] - self.bubble_spacing
def check_collisions(self, bubble, bubbles):
distance = self.outline_distance(bubble, bubbles)
return len(distance[distance < 0])
Definir o Método de Colisão de Bolhas
A classe BubbleChart também contém um método para verificar colisões de bolhas e mover-se em torno das bolhas colidindo. O método collides_with calcula a distância entre uma nova posição de bolha e as posições de outras bolhas. Se a distância for menor que zero, significa que há uma colisão, e o método retorna o índice da bolha colidindo. O método collapse move as bolhas em direção ao centro de massa e em torno das bolhas colidindo, e o método plot desenha as bolhas no gráfico.
def collides_with(self, bubble, bubbles):
distance = self.outline_distance(bubble, bubbles)
idx_min = np.argmin(distance)
return idx_min if type(idx_min) == np.ndarray else [idx_min]
def collapse(self, n_iterations=50):
"""
Mover as bolhas para o centro de massa.
Parâmetros
----------
n_iterations : int, default: 50
Número de movimentos a serem realizados.
"""
for _i in range(n_iterations):
moves = 0
for i in range(len(self.bubbles)):
rest_bub = np.delete(self.bubbles, i, 0)
## try to move directly towards the center of mass
## direction vector from bubble to the center of mass
dir_vec = self.com - self.bubbles[i, :2]
## shorten direction vector to have length of 1
dir_vec = dir_vec / np.sqrt(dir_vec.dot(dir_vec))
## calculate new bubble position
new_point = self.bubbles[i, :2] + dir_vec * self.step_dist
new_bubble = np.append(new_point, self.bubbles[i, 2:4])
## check whether new bubble collides with other bubbles
if not self.check_collisions(new_bubble, rest_bub):
self.bubbles[i, :] = new_bubble
self.com = self.center_of_mass()
moves += 1
else:
## try to move around a bubble that you collide with
## find colliding bubble
for colliding in self.collides_with(new_bubble, rest_bub):
## calculate direction vector
dir_vec = rest_bub[colliding, :2] - self.bubbles[i, :2]
dir_vec = dir_vec / np.sqrt(dir_vec.dot(dir_vec))
## calculate orthogonal vector
orth = np.array([dir_vec[1], -dir_vec[0]])
## test which direction to go
new_point1 = (self.bubbles[i, :2] + orth *
self.step_dist)
new_point2 = (self.bubbles[i, :2] - orth *
self.step_dist)
dist1 = self.center_distance(
self.com, np.array([new_point1]))
dist2 = self.center_distance(
self.com, np.array([new_point2]))
new_point = new_point1 if dist1 < dist2 else new_point2
new_bubble = np.append(new_point, self.bubbles[i, 2:4])
if not self.check_collisions(new_bubble, rest_bub):
self.bubbles[i, :] = new_bubble
self.com = self.center_of_mass()
if moves / len(self.bubbles) < 0.1:
self.step_dist = self.step_dist / 2
def plot(self, ax, labels, colors):
"""
Desenhar o gráfico de bolhas.
Parâmetros
----------
ax : matplotlib.axes.Axes
labels : list
Rótulos das bolhas.
colors : list
Cores das bolhas.
"""
for i in range(len(self.bubbles)):
circ = plt.Circle(
self.bubbles[i, :2], self.bubbles[i, 2], color=colors[i])
ax.add_patch(circ)
ax.text(*self.bubbles[i, :2], labels[i],
horizontalalignment='center', verticalalignment='center')
Criar Objeto BubbleChart e Plotar o Gráfico
Para criar o gráfico de bolhas empacotadas, precisamos criar um objeto BubbleChart e chamar o método collapse para mover as bolhas em direção ao centro de massa. Em seguida, podemos criar uma figura matplotlib e adicionar um objeto de eixos a ela. Finalmente, chamamos o método plot para desenhar as bolhas no gráfico.
bubble_chart = BubbleChart(area=browser_market_share['market_share'],
bubble_spacing=0.1)
bubble_chart.collapse()
fig, ax = plt.subplots(subplot_kw=dict(aspect="equal"))
bubble_chart.plot(
ax, browser_market_share['browsers'], browser_market_share['color'])
ax.axis("off")
ax.relim()
ax.autoscale_view()
ax.set_title('Browser market share')
plt.show()
Resumo
Neste laboratório, aprendemos como criar um gráfico de bolhas empacotadas usando Matplotlib em Python. Definimos os dados como um dicionário contendo os nomes dos navegadores, a participação de mercado e a cor para cada bolha. Em seguida, criamos uma classe BubbleChart para configurar as posições iniciais das bolhas, calcular a distância máxima do passo, mover as bolhas em direção ao centro de massa e verificar colisões com outras bolhas. Finalmente, plotamos o gráfico usando matplotlib.