Введение
В этом практическом занятии мы научимся создавать график с упакованными пузырьками с использованием Matplotlib в Python. График с упакованными пузырьками представляет собой тип графика, на котором данные отображаются в виде пузырьков разного размера, при этом размер пузырька соответствует величине данных. Этот график полезен для отображения скалярных данных, то есть данных, которые представляют собой одно числовое значение, связанное с элементом.
Советы по работе с ВМ
После запуска виртуальной машины щелкните в левом верхнем углу, чтобы переключиться на вкладку Ноутбук и получить доступ к Jupyter Notebook для практики.
Иногда может потребоваться подождать несколько секунд, пока Jupyter Notebook не загрузится полностью. Валидация операций не может быть автоматизирована из-за ограничений Jupyter Notebook.
Если вы сталкиваетесь с проблемами во время обучения, не стесняйтесь обращаться к Labby. Оставьте отзыв после занятия, и мы оперативно решим проблему для вас.
Импортируем необходимые библиотеки
Для создания графика с упакованными пузырьками нам нужно импортировать библиотеки matplotlib.pyplot и numpy. Библиотека numpy используется для выполнения математических операций с массивами, что полезно для вычисления размеров пузырьков.
import matplotlib.pyplot as plt
import numpy as np
Определяем данные
Данные, которые мы будем использовать в этом примере, представляют собой долю рынка различных десктопных браузеров. Мы определим данные в виде словаря, содержащего названия браузеров, долю рынка и цвет для каждого пузырька.
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']
}
Определяем класс BubbleChart
Класс BubbleChart используется для создания графика с упакованными пузырьками. Класс принимает массив площадей пузырьков и значение расстояния между пузырьками. Метод __init__ настраивает начальные позиции пузырьков и вычисляет максимальное расстояние шага, которое представляет собой расстояние, на которое каждый пузырек может переместиться за одну итерацию.
class BubbleChart:
def __init__(self, area, bubble_spacing=0):
"""
Настройка для сжатия пузырьков.
Параметры
----------
area : array-like
Площадь пузырьков.
bubble_spacing : float, по умолчанию: 0
Минимальное расстояние между пузырьками после сжатия.
Примечания
-----
Если "area" отсортирован, результаты могут выглядеть странно.
"""
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
## вычисляем начальную сеточную разметку для пузырьков
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()
Определяем методы движения пузырьков
Класс BubbleChart также содержит методы для перемещения пузырьков к центру масс и проверки на столкновения с другими пузырьками. Метод center_of_mass вычисляет центр масс всех пузырьков, а метод center_distance вычисляет расстояние между пузырьком и центром масс. Метод outline_distance вычисляет расстояние между контурами пузырька и контурами других пузырьков, а метод check_collisions проверяет, сталкивается ли новая позиция пузырька с другими пузырьками.
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])
Определяем метод столкновения пузырьков
Класс BubbleChart также содержит метод для проверки столкновений пузырьков и перемещения вокруг столкнувшихся пузырьков. Метод collides_with вычисляет расстояние между новой позицией пузырька и позициями других пузырьков. Если расстояние меньше нуля, это означает, что есть столкновение, и метод возвращает индекс столкнувшегося пузырька. Метод collapse перемещает пузырьки к центру масс и вокруг столкнувшихся пузырьков, а метод plot рисует пузырьки на графике.
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):
"""
Перемещает пузырьки к центру масс.
Параметры
----------
n_iterations : int, по умолчанию: 50
Количество перемещений для выполнения.
"""
for _i in range(n_iterations):
moves = 0
for i in range(len(self.bubbles)):
rest_bub = np.delete(self.bubbles, i, 0)
## пытаемся переместиться напрямую к центру масс
## вектор направления от пузырька к центру масс
dir_vec = self.com - self.bubbles[i, :2]
## укорачиваем вектор направления, чтобы длина была равна 1
dir_vec = dir_vec / np.sqrt(dir_vec.dot(dir_vec))
## вычисляем новую позицию пузырька
new_point = self.bubbles[i, :2] + dir_vec * self.step_dist
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()
moves += 1
else:
## пытаемся переместиться вокруг пузырька, с которым сталкиваемся
## находим столкнувшийся пузырек
for colliding in self.collides_with(new_bubble, rest_bub):
## вычисляем вектор направления
dir_vec = rest_bub[colliding, :2] - self.bubbles[i, :2]
dir_vec = dir_vec / np.sqrt(dir_vec.dot(dir_vec))
## вычисляем ортогональный вектор
orth = np.array([dir_vec[1], -dir_vec[0]])
## проверяем, в какую сторону идти
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):
"""
Рисует график с пузырьками.
Параметры
----------
ax : matplotlib.axes.Axes
labels : list
Метки для пузырьков.
colors : list
Цвета для пузырьков.
"""
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')
Создаем объект BubbleChart и рисуем график
Для создания графика с упакованными пузырьками нам нужно создать объект BubbleChart и вызвать метод collapse, чтобы переместить пузырьки к центру масс. Затем мы можем создать фигуру matplotlib и добавить в нее объект оси. Наконец, мы вызываем метод plot, чтобы нарисовать пузырьки на графике.
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()
Резюме
В этом практическом занятии мы узнали, как создавать график с упакованными пузырьками с использованием Matplotlib в Python. Мы определили данные в виде словаря, содержащего названия браузеров, долю рынка и цвет для каждого пузырька. Затем мы создали класс BubbleChart, чтобы настроить начальные позиции пузырьков, вычислить максимальное расстояние шага, переместить пузырьки к центру масс и проверить на столкновения с другими пузырьками. Наконец, мы построили график с использованием matplotlib.