数据可视化(四)

制作世界人口地图

提取相关的数据

我们首先导入了模块json ,以便能够正确地加载文件中的数据,然后,我们将数据存储在pop_data 中。函数json.load() 将数据转换为Python能够处理的格式, 这里是一个列表。我们遍历pop_data 中的每个元素。每个元素都是一个字典,包含四个键—值对,我们将每个字典依次存储在pop_dict 中。我们检查字典的’Year’ 键对应的值是否是2010(由于population_data.json中的值都是用引号括起的,因此我们执行的是字符串比较)。如果年份为2010,我们就将与’Country Name’ 相关联的值存储到country_name 中,并将与’Value’ 相关联的值存储在population 中。接下来,我们打印每个国家的名称和人口数量。
world_population.py

import json
# 将数据加载到一个列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)
    # 打印每个国家2010年的人口数量
    for pop_dict in pop_data:
        if pop_dict['Year'] == '2010':
            country_name = pop_dict['Country Name']
            population = pop_dict['Value']
            print(country_name + ": " + population)

将字符串转换为数字值

population_data.json中的每个键和值都是字符串。为处理这些人口数据,我们需要将表示人口数量的字符串转换为数字值,为此我们使用函数int()以及函数float() ,每个字符串都成功地转换成了浮点数,再转换为整数。以数字格式存储人口数量值后,就可以使用它们来制作世界人口地图了。
world_population.py

import json
# 将数据加载到一个列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)
    # 打印每个国家2010年的人口数量
    for pop_dict in pop_data:
        if pop_dict['Year'] == '2010':
            country_name = pop_dict['Country Name']
            population = int(float(pop_dict['Value']))
            print(country_name + ": " + str(population))

获取两个字母的国别码

制作地图前,还需要解决数据存在的最后一个问题。Pygal中的地图制作工具要求数据为特定的格式:用国别码表示国家,以及用数字表示人口数量。处理地理政治数据时,经常需要用到几个标准化国别码集。population_data.json中包含的是三个字母的国别码,但Pygal使用两个字母的国别码。我们需要想办法根据国家名获取两个字母的国别码。 Pygal使用的国别码存储在模块i18ninternationalization的缩写)中。字典COUNTRIES 包含的键和值分别为两个字母的国别码和国家名。要查看这些国别码,可从模块i18n 中导入这个字典,并打印其键和值:
countries.py

from pygal_maps_world.i18n import COUNTRIES
for country_code in sorted(COUNTRIES.keys()):
    print(country_code, COUNTRIES[country_code])

country_codes.py

from pygal_maps_world.i18n import COUNTRIES
def get_country_code(country_name):
    """根据指定的国家,返回Pygal使用的两个字母的国别码"""
    for code, name in COUNTRIES.items():
        if name == country_name:
            return code
        # 如果没有找到指定的国家,就返回None
    return None

接下来,在world_population.py中导入get_country_code
world_population.py

import json
from country_codes import get_country_code
# 将数据加载到一个列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)
    # 打印每个国家2010年的人口数量
    for pop_dict in pop_data:
        if pop_dict['Year'] == '2010':
            country_name = pop_dict['Country Name']
            population = int(float(pop_dict['Value']))
            code = get_country_code(country_name)
            if code:
                print(code + ": " + str(population))
            else:
                print('ERROR - ' + country_name)

提取国家名和人口数量后,我们将国别码存储在code 中,如果没有国别码,就在其中存储None 。如果返回了国别码,就打印国别码和相应国家的人口数量。 如果没有找到国别码,就显示一条错误消息,其中包含无法找到国别码的国家的名称。

制作世界地图

Pygal提供了图表类型maps ,可帮助你制作呈现各国数据的世界地图。创建一个突出北美、中美和南美的简单地图:
americas.py

import pygal
wm = pygal.maps.world.World()
wm.title = 'North, Central, and South America'
wm.add('North America', ['ca', 'mx', 'us'])
wm.add('Central America', ['bz', 'cr', 'gt', 'hn', 'ni', 'pa', 'sv'])
wm.add('South America', ['ar', 'bo', 'br', 'cl', 'co', 'ec', 'gf', 'gy', 'pe', 'py', 'sr', 'uy', 've'])
wm.render_to_file('americas.svg')

我们创建了一个maps 实例,并设置了该地图的的title 属性。接着我们使用了方法add() ,它接受一个标签和一个列表,其中后者包含我们要突出的国家 的国别码。每次调用add() 都将为指定的国家选择一种新颜色,并在图表左边显示该颜色和指定的标签。我们要以同一种颜色显示整个北美地区,因此第一次调用add() 时, 在传递给它的列表中包含'ca''mx''us' ,以同时突出加拿大、墨西哥和美国。接下来,对中美和南美国家做同样的处理。 最后使用方法render_to_file() 创建一个包含该图表的.svg文件,你可以在浏览器中打开它,运行结果如下:
在这里插入图片描述

在世界地图上呈现数字数据

na_populations.py

import pygal
wm = pygal.maps.world.World()
wm.title = 'Populations of Countries in North America'
wm.add('North America', {'ca': 34126000, 'us': 309349000, 'mx': 113423000})
wm.render_to_file('na_populations.svg')

首先,创建了一个maps.world.World实例并设置了标题。接下来,使用了方法add() ,但这次通过第二个实参传递了一个字典而不是列表。这个字典将两个字母的Pygal国别码作为键,将人口数量作为值。Pygal根据这些数字自动给不同国家着以深浅不一的颜色(人口最少的国家颜色最浅,人口最多的国家颜色最深),如下图所示:
这幅地图具有交互性:如果你将鼠标指向某个国家,将看到其人口数量。

绘制完整的世界人口地图

要呈现其他国家的人口数量,需要将前面处理的数据转换为Pygal要求的字典格式:键为两个字母的国别码,值为人口数量。
world_population.py

import json
from country_codes import get_country_code
import pygal
# 将数据加载到一个列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)
    # 创建一个包含人口数量的字典
    cc_populations = {}
    for pop_dict in pop_data:
        if pop_dict['Year'] == '2010':
            country_name = pop_dict['Country Name']
            population = int(float(pop_dict['Value']))
            code = get_country_code(country_name)
            if code:
                cc_populations[code] = population
wm = pygal.maps.world.World()
wm.title = 'World Population in 2010, by Country'
wm.add('2010', cc_populations)
wm.render_to_file('world_population.svg')

我们首先导入了pygal 。接着我们创建了一个空字典,用于以Pygal要求的格式存储国别码和人口数量。如果返回了国别码,就将国别码和人口数量分别作为键和值填充字典cc_populations 。我们创建了一个maps 实例,并设置其title 属性,调用了add() ,并向它传递由国别码和人口数量组成的字典。生成的地图如下:
在这里插入图片描述

根据人口数量将国家分组

印度和中国的人口比其他国家多得多,但在当前的地图中,它们的颜色与其他国家差别较小。中国和印度的人口都超过了10亿,接下来人口最多的国家是美国,但只有大约3亿。 下面不将所有国家都作为一个编组,而是根据人口数量分成三组——少于1000万的、介于1000万和10亿之间的以及超过10亿的:
world_population.py

import json
from country_codes import get_country_code
import pygal
# 将数据加载到一个列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)
    # 创建一个包含人口数量的字典
    cc_populations = {}
    for pop_dict in pop_data:
        if pop_dict['Year'] == '2010':
            country_name = pop_dict['Country Name']
            population = int(float(pop_dict['Value']))
            code = get_country_code(country_name)
            if code:
                cc_populations[code] = population
    # 根据人口数量将所有的国家分成三组
    cc_pops_1, cc_pops_2, cc_pops_3 = {}, {}, {}
    for cc, pop in cc_populations.items():
        if pop < 10000000:
            cc_pops_1[cc] = pop
        elif pop < 1000000000:
            cc_pops_2[cc] = pop
        else:
            cc_pops_3[cc] = pop
    # 看看每组分别包含多少个国家
print(len(cc_pops_1), len(cc_pops_2), len(cc_pops_3))
wm = pygal.maps.world.World()
wm.title = 'World Population in 2010, by Country'
wm.add('0-10m', cc_pops_1)
wm.add('10m-1bn', cc_pops_2)
wm.add('>1bn', cc_pops_3)
wm.render_to_file('world_population.svg')

为将国家分组,我们创建了三个空字典。接下来,遍历cc_populations ,检查每个国家的人口数量。if-elif-else 代码块将每个国别码-人口数量对加入到合适的字典(cc_pops_1 、cc_pops_2 或cc_pops_3 )中。 接着我们打印这些字典的长度,以获悉每个分组的规模。绘制地图时,我们将全部三个分组都添加到maps中。如果你现在运行这个程序,首先看到的将是每个分组的规模:
在这里插入图片描述
上述输出表明,人口少于1000万的国家有85个,人口介于1000万和10亿之间的国家有69个,还有两个国家比较特殊,其人口都超过了10亿。这样的分组看起来足够了,让地图包含丰富的信息。下图显示了生成的地图:
在这里插入图片描述

使用Pygal设置世界地图的样式

在这个地图中,根据人口将国家分组虽然很有效,但默认的颜色设置很难看。下面使用Pygal样式设置指令来调整颜色。 我们也让Pygal使用一种基色,但将指定该基色,并让三个分组的颜色差别更大:
world_population.py

import json
from country_codes import get_country_code
from pygal.style import RotateStyle
import pygal
# 将数据加载到一个列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)
    # 创建一个包含人口数量的字典
    cc_populations = {}
    for pop_dict in pop_data:
        if pop_dict['Year'] == '2010':
            country_name = pop_dict['Country Name']
            population = int(float(pop_dict['Value']))
            code = get_country_code(country_name)
            if code:
                cc_populations[code] = population
    # 根据人口数量将所有的国家分成三组
    cc_pops_1, cc_pops_2, cc_pops_3 = {}, {}, {}
    for cc, pop in cc_populations.items():
        if pop < 10000000:
            cc_pops_1[cc] = pop
        elif pop < 1000000000:
            cc_pops_2[cc] = pop
        else:
            cc_pops_3[cc] = pop
    # 看看每组分别包含多少个国家
print(len(cc_pops_1), len(cc_pops_2), len(cc_pops_3))
wm_style = RotateStyle('#336699')
wm = pygal.maps.world.World(style=wm_style)
wm.title = 'World Population in 2010, by Country'
wm.add('0-10m', cc_pops_1)
wm.add('10m-1bn', cc_pops_2)
wm.add('>1bn', cc_pops_3)
wm.render_to_file('world_population.svg')

Pygal样式存储在模块style 中,我们从这个模块中导入了样式RotateStyle 。创建这个类的实例时,需要提供一个实参——十六进制的RGB颜色;Pygal将根 据指定的颜色为每组选择颜色。十六进制格式的RGB颜色是一个以井号(#)打头的字符串,后面跟着6个字符,其中前两个字符表示红色分量,接下来的两个表示绿色分量,最 后两个表示蓝色分量。每个分量的取值范围为00 (没有相应的颜色)~FF (包含最多的相应颜色)。如果你在线搜索hex colorchooser(十六进制颜色选择器 ),可找到让你能够尝试选择不同的颜色并显示其RGB值的工具。这里使用的颜色值(#336699)混合了少量的红色(33)、多一些的绿色(66)和更多一些的蓝色(99),它为RotateStyle 提供了一种淡蓝色基色。
RotateStyle 返回一个样式对象,我们将其存储在wm_style 中。为使用这个样式对象,我们在创建maps 实例时以关键字实参的方式传递它。
前面的样式设置让地图的颜色更一致,也更容易区分不同的编组。运行结果如下:
在这里插入图片描述

加亮颜色主题

Pygal通常默认使用较暗的颜色主题。为方便印刷,我使用LightColorizedStyle 加亮了地图的颜色。这个类修改整个图表的主题,包括背景色、标签以及各个国家的颜色。 要使用这个样式,先导入它:

from pygal.style import LightColorizedStyle

然后就可独立地使用LightColorizedStyle 了,例如:

wm_style = LightColorizedStyle

然而使用这个类时,你不能直接控制使用的颜色,Pygal将选择默认的基色。要设置颜色,可使用RotateStyle ,并将LightColorizedStyle 作为基本样式。为此,导入LightColorizedStyleRotateStyle

from pygal.style import LightColorizedStyle, RotateStyle

再使用RotateStyle 创建一种样式,并传入另一个实参base_style

wm_style = RotateStyle('#336699', base_style=LightColorizedStyle)

这设置了较亮的主题,同时根据通过实参传递的颜色给各个国家着色。使用这种样式时,生成的图表与本书的屏幕截图更一致。 尝试为不同的可视化选择合适的样式设置指令时,在import 语句中指定别名会有所帮助:

from pygal.style import LightColorizedStyle as LCS, RotateStyle as RS

这样,样式定义将更短:

wm_style = RS('#336699', base_style=LCS)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值