前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在模仿中精进数据可视化08:哪个省份的学子是熬夜冠军?

在模仿中精进数据可视化08:哪个省份的学子是熬夜冠军?

作者头像
Python大数据分析
发布2022-04-03 13:30:37
6120
发布2022-04-03 13:30:37
举报

❝本文完整代码及数据已上传至我的Github仓库https://github.com/CNFeffery/FefferyViz ❞

1 简介

大家好~热衷于钻研复刻优秀数据可视化作品的费老师我🧐,最近的业余时间主要沉迷于撰写「Python+Dash快速web应用开发」系列文章,「在模仿中精进数据可视化」系列文章有两个月没更新了,今天继续捡起来🥳。

我们今天要复刻的数据可视化作品,是前段时间在微博刷屏的下面这张网易数读的作品,基于作业帮的用户画像数据对哪个地方的学习是“熬夜冠军”进行了可视化表达:

图1

而下面我们就来基于matplotlib,复刻出这幅作品~

2 复刻过程

2.1 拆解主要视觉元素

其实这幅作品有些类似于我们这个系列文章开篇那一期「贝壳研究院」的图,都是以半边扇形为主体构图元素,在极坐标中对数据进行一系列表达,而今天的案例我们构建扇形图表选择的是matplotlib中的「极坐标系」,非常简单方便。

按照惯例,我们先来“肢解”一下这幅图的主要构图元素:

  • 「多子图组合」

这幅作品中主要可以分为「主体扇形」子图和右下角略微“出墙来”的「点缀扇形」子图构成,我们可以使用plt.subplots()创建底层画板之后,再分别用fig.add_axes(rect, polar=True)来在不同位置插入不同大小的上述子图;

  • 「主体扇形底色交替填充」

首先我们可以观察到在这幅图的「主体扇形」右半圆中,背景色是由颜色交替切换的子扇形区域构成的,且仔细观察可以发现子扇形之间的交界处是有白色边界线的。

这部分我们就可以使用到matplotlib中的fill_between()区域色彩填充功能,先生成指定数量的右半圆「等弧度」集合,其作用于「极坐标系」时传入的第一个参数为「角度范围」,第二个参数为「填充起点半径值」,第三个参数为「填充终点半径值」,白色交界线直接使用plot()绘制直线即可;

  • 「极坐标柱状图与中央虚线」

在上述构建的交替底色的基础上,我们继续来将每个地区的数值映射为极坐标柱状图的柱体高度,注意,这里的柱体颜色也是交替切换的,并且需要给每个柱体中央添加虚线点缀;

  • 「主体扇形多规则文字标注」

在原作品中的「地区」「深夜学习活跃指数」在角度旋转上有三种规则方式,我们可以在一开始构建数据时针对不同排名的地区,打上用于区别类型的标签,好在之后的绘图过程中分别控制角度旋转计算方式:

图2

图3

至于其他的点缀元素,就不详细说了,文章结尾的绘图代码里都有详细的注释。

2.2 完成复刻

在上述拆解的基础上,我们就可以充分运用弧度跟角度之间的转换,配合matplotlibnumpy来复刻出下面的效果啦,最后裁剪出的作品如下,是不是相当还原呢~:

图4

再放一张没有拆掉“脚手架”(坐标轴线)的效果,你就会更加清楚我的构图逻辑了:

图5

完整代码如下,如有疑问欢迎在评论区与我进行交流:

代码语言:javascript
复制
# 生成每份子扇形区域的两边夹角
# 这里[::-1]是为了迎合matplotlib极坐标默认的角度位置
theta_group = (np.linspace(-0.5, 0.5, 32)*np.pi)[::-1]

# 创建图床和原始axes对象
fig, ax = plt.subplots(figsize=(10, 10))

############################
# 主体部分
############################

# 向原始图床中插入极坐标系新axes对象
ax1 = fig.add_axes([-0.5, 0, 1, 1], polar=True)

# 绘制右半边扇形区域最底层错落的色带填充
for idx, group in enumerate(fc.pairwise(theta_group)):
    
    # 当下标为偶数时,填充#e3effd色
    if idx % 2 == 0:
        ax1.fill_between(group, 0.75, 3, facecolor='#e3effd')
    # 当下标为奇数时,填充#fafbff色
    else:
        ax1.fill_between(group, 0.75, 3, facecolor='#fafbff')

# 绘制每份子扇形区域的中央虚线
for idx, group in enumerate(fc.pairwise(theta_group)):
    theta = (group[0] + group[1]) / 2
    ax1.plot([theta, theta], [0.75, 2.68], linestyle='--', color='#9fa0a0',  linewidth=0.25)

# 绘制极坐标柱状图,分别占据每份子扇形区域的对应外扩长度
for idx, group in enumerate(fc.pairwise(theta_group)):
    theta = (group[0] + group[1]) / 2

    ax1.bar([theta], [2.25*data.at[idx, '深夜学习活跃指数']*0.01], 
            width=[np.pi / 32], bottom=0.75, 
            # 对下标分别为偶数与奇数的扇形绘制不同颜色
            facecolor='#6785f2' if idx % 2 != 0 else '#7171fe', 
            edgecolor='white', linewidth=0.1, alpha=0.95, zorder=9)

# 绘制子扇形区域之间交界处的白色边界
for theta in theta_group:
    ax1.plot([theta, theta], [1, 3], color='white',  linewidth=0.2)
    
def rotate_text(text, group, method):
    
    if method == 1:
        return text, ((group[0] + group[1]) * 0.5 / np.pi) * 180 - 90
    
    elif method == 2:
        return '\n'.join(list(text)), ((group[0] + group[1]) * 0.5 / np.pi) * 180
    
    elif method == 3:
        return text, ((group[0] + group[1]) * 0.5 / np.pi) * 180 - 90 + 180
    

# 地区+数值文字标注
for idx, group in enumerate(fc.pairwise(theta_group[::-1])):
    # 控制向data表的索引不越界
    if idx < 31:
        
        # 控制第一名的特殊字体颜色
        if data.at[30-idx, '地区'] == '江苏':
            text_color, value_color = 'white', 'white'
        else:
            text_color, value_color = 'black', '#595757'
        
        # 利用前面定义的自编函数生成对应的文字与旋转角度
        text, angle = rotate_text(data.at[30-idx, '地区'], group, method=data.at[30-idx, '文字排布'])
        
        # 标注地区名称
        ax1.annotate(text, xy=[(group[0]+group[1]) / 2, 2.925], 
                     va='center', ha='center', zorder=10,
                     color=text_color,
                     rotation=angle,
                     fontsize=11)
        
        # 标注深夜学习活跃指数
        ax1.annotate(re.sub('\.$', '', str(data.at[30-idx, '深夜学习活跃指数'])[:4]), 
                     xy=[(group[0]+group[1]) / 2, 2.79], 
                     va='center', ha='center', zorder=10,
                     rotation=angle,
                     fontsize=10,
                     color=value_color,
                     fontproperties='Times New Roman')

# 绘制外围黑色虚线
ax1.plot(np.linspace(-0.38, 0.45, 1000)*np.pi, [3.275]*1000, 
         linestyle='dashed', color='#595655', linewidth=0.75)

# 添加“0~2点学习活跃指数”标注
ax1.annotate('\n'.join(list('0~2点学习活跃指数')), 
             xy=[0, 3.21], 
             va='center', 
             ha='center', 
             ma='center',
             zorder=10,
             rotation=0,
             fontsize=11,
             color='black',
             fontproperties='Microsoft Yahei',
             fontweight='bold',
             bbox=dict(boxstyle="round", fc="white", ec="white", alpha=1))

############################
# 右下角点缀
############################

# 向原始图床中插入极坐标系新axes对象
ax2 = fig.add_axes([0.25, -0.7, 1, 1], polar=True)

theta_group2 = (np.linspace(-1.5, -0.5, 32)*np.pi)[::-1]

# 绘制左半边扇形区域最底层错落的色带填充
for idx, group in enumerate(fc.pairwise(theta_group2)):
    
    # 当下标为偶数时,填充#e3effd色
    if idx % 2 == 0:
        ax2.fill_between(group, 0.75, 3, facecolor='#e3effd')
    # 当下标为奇数时,填充#fafbff色
    else:
        ax2.fill_between(group, 0.75, 3, facecolor='#fafbff')

# 紧凑布局
fig.tight_layout(pad=0)

# 关闭所有axes的坐标轴线

ax.axis('off')
ax1.axis('off')
ax2.axis('off')

# 导出为图片
fig.savefig('图4.png', dpi=500, bbox_inches='tight', pad_inches=0, facecolor='white')
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python大数据分析 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 简介
  • 2 复刻过程
    • 2.1 拆解主要视觉元素
      • 2.2 完成复刻
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档