专栏首页Crossin的编程教室数据可视化:疫情期间市值增长top25公司

数据可视化:疫情期间市值增长top25公司


1 简介

大家好,欢迎来到Crossin的编程教室!

今年肆虐全球的新冠疫情给世界各国经济都造成了相当大的影响,尤其对很多实体经济带来巨大冲击。不过,这期间也有不少公司保持了较高的增长。前段时间我看到一张数据图表(图1),针对2020年1月1日到6月16日之间,世界范围内市值增大最多的25家公司进行可视化:

图1

这样一张典型的商业图表,看起来形式巧妙,且表现出很多数据信息。今天的文章中,我就将带大家学习如何利用matplotlib来条理清楚地制作出这种类型的可视化作品。

2 模仿过程

首先我们来分析一下原作品的元素构成:

  • 「立体感的营造」

其实原作品咋一看起来的立体感,只是玩了个花招,我们本质上只需要创建出最左列竖直方向上等分25份的填充区域,再向右偏移适合的距离后,缩小竖直方向上的总体范围再25等分,最后将这两部分等分的填充区域连接起来,最后再为中间的连接区域蒙上一层等大小的带透明度的暗色蒙版即可~

  • 「logo与国旗图片的插入」

原作品中众多图片,只要仔细观察就可以发现是手动PS上去的,存在着一些微小的瑕疵,而我们既然要用matplotlib来制作这张图,当然直接写循环控制图片的插入即可。

matplotlib中向画板插入其他图片有很多方法,我们为了控制好众多logo之间的协调,可以使用matplotlib中的inset_axes()来插入指定位置和尺寸的子图。

  • 「数值标注的控制」

原作品中不同公司市值增长的不同体现在不同长度柱体以及不同大小文字标注的映射之上的,我们可以配合简单的归一化变换,来约束字体和柱体长度的映射。

搞明白原作品中主要元素的实现方式之后,我们就来动手了。

首先来读入原始数据,这次我们使用的是现成的数据,保存在excel表格中(下载地址见文末):

import matplotlib.pyplot as plt
import pandas as pd

# 设置默认字体
plt.rcParams['font.sans-serif'] = ['Times New Roman']

raw = pd.read_excel('data.xlsx')
raw.head()

图2

接着为了方便处理公司类型向指定配色的映射,我们先来创建一个映射字典:

type2color = {
    'Technology': '#e2a080',
    'E-Commerce': '#ebb66a',
    'Automotive': '#c198ba',
    'Finance': '#aab5d8',
    'Tele-communications': '#bdd7e4',
    'Media': '#efcfde',
    'Software': '#d5c1c4',
    'Pharmaceutical': '#f9e4ad',
    'Alcohol': '#c3d3ac',
    'Retail': '#88bb70'
}

而为了创建出原作品中最重要的不同条带,我们可以配合matplotlib中的fill_between()

而为了处理好左侧与右侧的竖直方向25等分区域,我们可以在对原数据每一行循环的过程中,自定义下列函数来计算区域范围:

def create_fill_area(row, top_y=0.8, bottom_y=0.01):
    
    # 初始化包围填充区域的上下线条y坐标
    line1, line2 = [1 - 0.04*row, 1 - 0.04*row], [1- 0.04*(row+1), 1- 0.04*(row+1)]
    
    # 追加阴影段y坐标
    line1.append(0.01 + (25 - row) * (0.8 - 0.01) / 25)
    line2.append(0.01 + (25 - row - 1) * (0.8 - 0.01) / 25)
    
    # 追加最后一段平行段y坐标
    line1.append(0.01 + (25 - row) * (0.8 - 0.01) / 25)
    line2.append(0.01 + (25 - row - 1) * (0.8 - 0.01) / 25)
    
    return line1, line2

做好这些准备工作之后,剩余的绘图过程就很简单了,最终得到的模仿作品如下:

图3

完整代码如下,虽然看起来略多,其实大部分都是重复的逻辑传入不同的参数而已,还是比较简单的:

fig, ax = plt.subplots(figsize=(4.8, 6))

ax.set_xlim(0, 1.01)
ax.set_ylim(0, 1)
for row in range(raw.shape[0]):
    
    # 定义区域填充对应的x坐标
    x = [0, 0.15, 0.215, 0.6+raw.at[row, 'Grown'] / 1000]
    
    # 生成区域填充对应的y坐标
    line1, line2 = create_fill_area(row)
    
    # 对指定区域进行填充
    ax.fill_between(x,
                    line1,
                    line2,
                    color=type2color[raw.at[row, 'Type']],
                    edgecolor='none')
    
    # 从logo文件夹下读取对应logo图片
    try:
        logo = plt.imread(f'logo/{raw.at[row, "Company"]}.png')
    except FileNotFoundError:
        logo = plt.imread(f'logo/{raw.at[row, "Company"]}.jpg')
    
    # 插入公司logo
    ax_logo = ax.inset_axes((0.05, 1 - 0.04*(row+1)+0.005, 0.08, 0.025))
    ax_logo.imshow(logo)
    
    ax_logo.axis('off')
    ax_logo.set_facecolor(type2color[raw.at[row, 'Type']])
    
    # 处理单个及多个国家情况下的国旗绘制
    for idx, country in enumerate(raw.at[row, 'Country'].split('&')[::-1]):
        
        # 读取对应国旗图片
        flag = plt.imread(f'flag/{country}.png')
        
        # 插入国旗子图
        ax_flag = ax.inset_axes((0.545-idx*0.06, 0.013+(25 - row - 1)*((0.8 - 0.01) / 25), 0.1, 0.025))
        ax_flag.imshow(flag)

        ax_flag.axis('off')
        ax_flag.set_facecolor(type2color[raw.at[row, 'Type']])
    
    # 绘制排名
    ax.text(0.025, (1 - 0.04*row + 1 - 0.04*(row+1)) / 2, str(row+1), 
            ha='center', va='center',
            fontsize=5, color='black')
    
    # 绘制公司名称
    ax.text(0.215+0.01, 0.5 * (0.01 + (25 - row - 1) * (0.8 - 0.01) / 25 + 0.01 + (25 - row) * (0.8 - 0.01) / 25), 
            raw.at[row, 'Company'], 
            ha='left', va='center',
            fontsize=6, color='#494948', 
            weight='bold')
    
    # 处理第一名文字在填充区域内部,其余文字在填充区域外的情况
    if raw.at[row, 'Company'] == 'Amazon':
        ax.text(1, 0.5 * (0.01 + (25 - row) * (0.8 - 0.01) / 25 
                          + 0.01 + (25 - row - 1) * (0.8 - 0.01) / 25)-0.0025,
                '$'+str(raw.at[row, 'Grown'])+'B',
                color='white',
                fontsize=10,
                ha='right',
                va='center',
                weight='bold')
    
    else:
        # 配合归一化对字体进行大小映射
        ax.text(0.6+raw.at[row, 'Grown'] / 1000 + 0.01, 
                0.5 * (0.01 + (25 - row) * (0.8 - 0.01) / 25 + 0.01 + (25 - row - 1) * (0.8 - 0.01) / 25)-0.0025,
                '$'+str(raw.at[row, 'Grown'])+'B',
                color=type2color[raw.at[row, 'Type']],
                fontsize=5+((raw.at[row, 'Grown'] - raw['Grown'].min()) 
                            / (raw['Grown'].max() - raw['Grown'].min())) * 5,
                ha='left',
                va='center',
                weight='bold')
    
# 对指定区域进行带透明度的黑色蒙版,以达到阴影效果
ax.fill_between([0.15, 0.215],
                [0, 0.01],
                [1, 0.8],
                color='black',
                alpha=0.2, # 设置透明度
                edgecolor='none')

# 补充其余文字标注
ax.text(0.215+0.01, 0.805, 'Company', 
        color='#565555', fontsize=5,
        ha='left')

ax.text(0.6, 0.805, 'Country', 
        color='#565555', fontsize=5,
        ha='center')

# 补充上方数值刻度
ax.text(0.6, 0.825, '0', 
        color='#a9a8a8', fontsize=4,
        ha='center')
    
for i in range(1, 5):
    ax.text(0.6+0.1*i, 0.825, f'${i}00B', 
            color='#a9a8a8', fontsize=4,
            ha='center')
    
    ax.vlines(0.6+0.1*i, 0.01, 0.82, 
              color='#dcdcdb', linewidth=0.2)


ax.set_xticks([])
ax.set_yticks([])
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')

# 补充下排图例
ax_bar1 = ax.inset_axes((0.215, 0.88, 0.57, 0.02), transform=ax.transAxes)
ax_bar1.set_xlim(-0.45, 4.45)
ax_bar1.bar(range(5), height=1, width=0.9, 
            color=['#efcfde', '#d5c1c4', '#f9e4ad', '#c3d3ac', '#88bb70'])

ax_bar1.set_xticks(range(5))
ax_bar1.set_xticklabels(['Media', 'Software', 'Pharmaceutical', 'Alcohol', 'Retail'],
                        fontsize=5, color='#4f4e4e', weight='bold')

ax_bar1.set_yticks([])

ax_bar1.spines['left'].set_color('none')
ax_bar1.spines['right'].set_color('none')
ax_bar1.spines['top'].set_color('none')
ax_bar1.spines['bottom'].set_color('none')
ax_bar1.tick_params(color='none', pad=-2)

ax_bar1.set_facecolor('#f8f8f8')

# 补充上排图例
ax_bar2 = ax.inset_axes((0.215, 0.98, 0.57, 0.02), transform=ax.transAxes)
ax_bar2.set_xlim(-0.45, 4.45)
ax_bar2.bar(range(5), height=1, width=0.9, 
            color=['#e2a080', '#ebb66a', '#c198ba', '#aab5d8', '#bdd7e4'])

ax_bar2.set_xticks(range(5))
ax_bar2.set_xticklabels(['Technology', 'E-Commerce', 'Automotive', 'Finance', 'Tele-\ncommunications'],
                        fontsize=5, color='#4f4e4e', weight='bold')

ax_bar2.set_yticks([])

ax_bar2.spines['left'].set_color('none')
ax_bar2.spines['right'].set_color('none')
ax_bar2.spines['top'].set_color('none')
ax_bar2.spines['bottom'].set_color('none')
ax_bar2.tick_params(color='none', pad=-2)

ax_bar2.set_facecolor('#f8f8f8')


ax.set_facecolor('#f8f8f8')
fig.set_facecolor('#f8f8f8')

fig.savefig('图3.png', dpi=800, bbox_inches='tight')

你可以自由尝试不同的配色方案,或者换成你的数据,快速制作出同样别致的可视化作品?~

本文完整代码及数据:https://github.com/CNFeffery/FefferyViz/tree/master/assets/05%EF%BC%9A%E7%96%AB%E6%83%85%E6%9C%9F%E9%97%B4%E5%B8%82%E5%80%BC%E5%A2%9E%E9%95%BFtop25%E5%85%AC%E5%8F%B8


本文分享自微信公众号 - Crossin的编程教室(crossincode)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-12-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • (在模仿中精进数据可视化05)疫情期间市值增长top25公司

      新冠疫情对很多实体经济带来冲击的同时,也给很多公司带来了新的增长点。前段时间我看到图1所示的数据可视化作品,针对2020年1月1日到6月16日之间,世界范围...

    Feffery
  • Python-matplotlib可视化设计:疫情期间市值增长top25公司

    新冠疫情对很多实体经济带来冲击的同时,也给很多公司带来了新的增长点。前段时间我看到图1所示的数据可视化作品,针对2020年1月1日到6月16日之间,世界范围内市...

    DataCharm
  • 穿越疫情高速扩张,SaaS行业正成为创业投资的焦点

    ? 来源:亿欧网 作者:王方玉 编辑:顾彦 ---- 腾讯SaaS加速器 二期30席项目招募 报名方式 腾讯SaaS加速器,作为腾讯产业加速器的一个重要组成...

    腾讯SaaS加速器
  • 复工后的消费反弹潮

    “疫情过后,我们一起看樱花”、“疫情过后我一定要使劲吃,到处逛”,网上到处都充满了对疫情结束后的种种向往,那么疫情过后究竟会不会掀起消费狂潮?富国大通认为,狂潮...

    庄帅
  • 2020全球软件SaaS云计算产业展望

    ? 来源:中信证券研究  文:许英博 陈俊云 杨泽原 ---- 美股经验表明,SaaS是兼具中长期高确定性、成长性的优质赛道,理想的市场空间、良好的竞争格局...

    腾讯SaaS加速器
  • 零售行业“金九银十”值得期待,消费迎来第二波发力?

    国家统计局发言人付凌晖在9月15日新闻发布会上表示,今年受疫情冲击,消费前期出现了较大幅度下滑,随着经济恢复,居民就业保持总体稳定,国内防控形势总体稳定,居民外...

    庄帅
  • 从估值角度看家电行业表现,家电龙头依然具备估值修复空间

    家用电器属于可选消费行业,所谓”可选消费”,是指的除食品、日用品等生活必需品以外的消费。

    庄帅
  • 疫情后期,什么样的企业能够强势吸金?

    “提示说明:数据猿最新发布产业全景图:2020中国数据智能产业图谱1.0版,欲获取超高清版大图,后台回复关键词“图谱”即可。

    数据猿
  • 不确定性猛增的 2020,海康威视回答了 36 个问题

    全球经济下行、业内需求不足、实体清单,2019年的经济环境并不明朗。在此背景下海康威视依旧成绩斐然,营业总收入576.58亿元,比上年同期增长15.69%。

    AI掘金志

扫码关注云+社区

领取腾讯云代金券