来自《Python编程:从入门到实践》的第二篇,对数据可视化方面的初步学习,主要包括绘制随机漫步图,绘制某地区最高温和最低温分布图,以及世界人口地图三个部分。
随机漫步
随机漫步 是这样行走得到的路径:每次行走都完全是随机的,没有明确的方向,结果是由一系列随机决策决定的。
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| from random import choice import matplotlib.pyplot as plt
class RandomWalk(): """生成一个随机漫步的类""" def __init__(self, num_points=5000): self.num_points = num_points self.x_values = [0] self.y_values = [0]
def fill_work(self): """生成漫步包含的点,并决定每次漫步的方向""" while len(self.x_values) < self.num_points: x_direction = choice([1, -1]) x_distance = choice([0, 1, 2, 3, 4]) x_step = x_direction * x_distance
y_direction = choice([1, -1]) y_distance = choice([0, 1, 2, 3, 4]) y_step = y_direction * y_distance
if x_step == 0 and y_step == 0: continue next_x = self.x_values[-1] + x_step next_y = self.y_values[-1] + y_step
self.x_values.append(next_x) self.y_values.append(next_y)
rw = RandomWalk(50000) rw.fill_work()
point_numbers = list(range(rw.num_points)) plt.scatter(rw.x_values, rw.y_values, c=point_numbers, cmap=plt.cm.Blues, edgecolors='none', s=1)
plt.scatter(0, 0, c='green', edgecolors='none', s=100) plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolors='none', s=100)
plt.axis('off')
plt.title("随机散点图") plt.show()
|
实现效果图:
<img src=”https://huanyouchen-1252081928.cos.ap-shanghai.myqcloud.com/2018-04-28-sui-ji-man-bu.png?imageView2/0/q/75|watermark/2/text/aHVhbnlvdWNoZW4uZ2l0aHViLmlv/font/5qW35L2T/fontsize/320/fill/IzBBMEEwQQ==/dissolve/93/gravity/SouthEast/dx/10/dy/10|imageslim", width=”60%” height=”60%”>
在着色部分,使用了range()
生成了一个数字列表,其中包含的数字个数与漫步包含的点数相同。接下来,将这个列表存储在 point_numbers
中,以便后面使用它来设置每个漫步点的颜色。然后将参数c
设置为 point_numbers ,指定使用颜色映射 Blues ,并传递实参 edgecolor=none
以删除每个点周围的轮廓。最终的随机漫步图从浅蓝色渐变为深蓝色,
某地区最高温和最低温
数据来源:https://nostarch.com/pythoncrashcourse/ 将该书配套资源下载下来后,在第16章文件夹中找到文件 death_valley_2014.csv
即可使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import csv from datetime import datetime from matplotlib import pyplot as plt
filename = 'death_valley_2014.csv' with open(filename) as f: reader = csv.reader(f) header_row = next(reader) dates, highs, lows = [], [], [] for row in reader: try: current_date = datetime.strptime(row[0], "%Y-%m-%d") high = int(row[1]) low = int(row[3]) except ValueError: print(current_date, 'misssing data') else: dates.append(current_date) highs.append(high) lows.append(low)
fig = plt.figure(dpi=128, figsize=(10, 6)) plt.plot(dates, highs, c='red', alpha=0.5) plt.plot(dates, lows, c='blue', alpha=0.5)
plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.2)
plt.title("2014年每日最高温和最低温", fontsize=18) plt.xlabel('', fontsize=14)
fig.autofmt_xdate() plt.ylabel("温度", fontsize=14)
plt.tick_params(axis='both', which='major', labelsize=12) plt.show()
|
实现效果图:
绘制世界人口地图
数据来源:https://huanyouchen-1252081928.cos.ap-shanghai.myqcloud.com/population_data.json
该json文件的内容是一个列表,里面每个元素都是一个包含四个键的字典:国家名、国别码、年份以及表示人口数量的值:
1 2 3 4 5 6 7 8 9 10
| [ { "Country Name": "Arab World", "Country Code": "ARB", "Year": "1960", "Value": "96388069" }, ... ... ]
|
Pygal 中的地图制作工具要求数据为特定的格式:用国别码表示国家,以及用数字表示人口数量。处理地理政治数据时,经常需要用到几个标准化国别码集。 population_data.json 中包含的是三个字母的国别码,但 Pygal 使用两个字母的国别码。我们需要想办法根据国家名获取两个字母的国别码。
原书中的这段:
Pygal 使用的国别码存储在模块 i18n ( internationalization 的缩写)中。字典 COUNTRIES 包含的键和值分别为两个字母的国别码和国家名。要查看这些国别码,可从模块 i18n 中导入这个字典,并打印其键和值
现在已经改变了,需要从pygal.maps.world
导入COUNTRIES
才能正确使用:
1 2 3 4 5 6 7 8 9 10 11 12
|
from pygal.maps.world import COUNTRIES
def get_country_code(country_names): """根据指定的国家名,返回两个字母的国别码""" for code, name in COUNTRIES.items(): if name == country_names: return code return None
|
countries
模块在 COUNTRIES
中查找并返回两个字母的国别码以便给Pygal使用。
然后编写world_population模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import json from pygal_maps_world.maps import World from countries import get_country_code
filename = 'population_data.json' with open(filename) as f: pop_data = json.load(f)
world_population_dict = {} for pop_dict in pop_data: if pop_dict['Year'] == '2010': country_name = pop_dict['Country Name'] code = get_country_code(country_name) population = int(float(pop_dict['Value'])) if code: world_population_dict[code] = population
world_pops_1, world_pops_2, world_pops_3 = {}, {}, {} for country, pops in world_population_dict.items(): if pops < 10000000: world_pops_1[country] = pops elif pops < 100000000: world_pops_2[country] = pops else: world_pops_3[country] = pops
world_map = World() world_map.title = "World Population in 2010, by country" world_map.add('0-1千万', world_pops_1) world_map.add('1千万-1亿', world_pops_2) world_map.add('1亿以上', world_pops_3) world_map.render_to_file('world_population.svg')
|
这里面有一个小知识点,在第16行,pop_dict['Value']
返回的可能是一个带有小数点的字符串。对于一个带有小数点的字符串,比如”234.176”,将其转换成整数234
,直接用int("234.176")
是会报错:ValueError: invalid literal for int()
,因为Python不能直接将包含小数点的字符串转换为整数,所以用float()
将字符串转换为小数,再用 int()
丢弃小数部分,返回一个整数。
实现效果: