专栏名称: 新语数据故事汇
《新语数据故事汇,数说新语》科普数据科学、讲述数据故事,深层次挖掘数据价值。
目录
相关文章推荐
51好读  ›  专栏  ›  新语数据故事汇

数据可视化:密集气泡图(Packed Bubbles)及python示例

新语数据故事汇  · 公众号  ·  · 2024-07-28 16:59

正文

密集气泡图(Packed Bubbles)类似于气泡图,但气泡被紧密排列,而不是分散在网格上。该图表通过一种算法尽可能将所有气泡靠近重心,同时调整气泡的位置以避免碰撞。密集气泡图允许您根据相对大小来可视化和比较数据,结合了词云和气泡图的特点,无需使用 x 轴和 y 轴。

Matplotlib 实现密集气泡图

在 Matplotlib 文档的杂项部分中有一个密集气泡图例子( https://matplotlib.org/3.5.2/gallery/misc/packed_bubbles.html )如下:

import numpy as npimport matplotlib.pyplot as plt
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']}

class BubbleChart: def __init__(self, area, bubble_spacing=0): """ Setup for bubble collapse.
Parameters ---------- area : array-like Area of the bubbles. bubble_spacing : float, default: 0 Minimal spacing between bubbles after collapsing.
Notes ----- If "area" is sorted, the results might look weird. """ 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()
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])
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): """ Move bubbles to the center of mass.
Parameters ---------- n_iterations : int, default: 50 Number of moves to perform. """ 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






请到「今天看啥」查看全文