在本章中,你将从网上下载数据,并对这些数据进行可视化。网上的数据多得难以置信,且大多未经过仔细检查。如果能够对这些数据进行分析,你就能发现别人没有发现的规律和关联。
我们将访问并可视化以两种常见格式存储的数据:CSV 和JSON。我们将使用Python模块csv 来处理以CSV(逗号分隔的值)格式存储的天气数据,找出两个不同地区在一段时间内的最高温度和最低温度。然后,我们将使用matplotlib根据下载的数据创建一个图表,展示两个不同地区的气温变化:阿拉斯加锡特卡和加利福尼亚死亡谷。在本章的后面,我们将使用模块json 来访问以JSON格式存储的人口数据,并使用Pygal绘制一幅按国别划分的人口地图。
阅读本章后,你将能够处理各种类型和格式的数据集,并对如何创建复杂的图表有更深入的认识。要处理各种真实世界的数据集,必须能够访问并可视化各种类型和格式的在线数据。
16.1CSV文件格式
要在文本文件中存储数据,最简单的方式是将数据作为一系列以逗号分隔的值 (CSV)写入文件。这样的文件称为CSV文件。例如,下面是一行CSV格式的天气数据:
2014-1-5,61,44,26,18,7,-1,56,30,9,30.34,30.27,30.15,,,,10,4,,0.00,0,,195
这是阿拉斯加锡特卡2014年1月5日的天气数据,其中包含当天的最高气温和最低气温,还有众多其他数据。CSV文件对人来说阅读起来比较麻烦,但程序可轻松地提取并处理其 中的值,这有助于加快数据分析过程。
我们将首先处理少量锡特卡的CSV格式的天气数据,这些数据可在本书的配套资源(https://www.nostarch.com/pythoncrashcourse/ )中找到。请将文件sitka_weather_07-2014.csv复制到 存储本章程序的文件夹中(下载本书的配套资源后,你就有了这个项目所需的所有文件)。
注意 这个项目使用的天气数据是从http://www.wunderground.com/history/ 下载而来的。
16.1.1分析CSV文件头
csv 模块包含在Python标准库中,可用于分析CSV文件中的数据行,让我们能够快速提取感兴趣的值。下面先来查看这个文件的第一行,其中包含一系列有关数据的描述:
highs_lows.py
import csv
filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
reader = csv.reader(f)
header_row = next(reader)
print(header_row)
导入模块csv 后,我们将要使用的文件的名称存储在filename 中。接下来,我们打开这个文件,并将结果文件对象存储在f 中(见❶)。然后,我们调用csv.reader() ,并将前面存储的文件对象作为实参传递给它,从而创建一个与该文件相关联的阅读器(reader )对象(见❷)。我们将这个阅读器对象存储在reader 中。
模块csv 包含函数next() ,调用它并将阅读器对象传递给它时,它将返回文件中的下一行。在前面的代码中,我们只调用了next() 一次,因此得到的是文件的第一行,其中包含文件头(见❸)。我们将返回的数据存储在header_row 中。正如你看到的,header_row 包含与天气相关的文件头,指出了每行都包含哪些数据:
['AKDT', 'Max TemperatureF', 'Mean TemperatureF', 'Min TemperatureF', 'Max Dew PointF', 'MeanDew PointF', 'Min DewpointF', 'Max Humidity', ' Mean Humidity', ' Min Humidity', ' Max Sea Level PressureIn', ' Mean Sea Level PressureIn', ' Min Sea Level PressureIn', ' Max VisibilityMiles', ' Mean VisibilityMiles', ' Min VisibilityMiles', ' Max Wind SpeedMPH', ' Mean Wind SpeedMPH', ' Max Gust SpeedMPH', 'PrecipitationIn', ' CloudCover', ' Events', ' WindDirDegrees']
reader处理文件中以逗号分隔的第一行数据,并将每项数据都作为一个元素存储在列表中。文件头AKDt 表示阿拉斯加时间(Alaska Daylight Time),其位置表明每行的第一个值都是日期或时间。文件头Max temperatureF 指出每行的第二个值都是当天的最高华氏温度。可通过阅读其他的文件头来确定文件包含的信息类型。
注意 文件头的格式并非总是一致的,空格和单位可能出现在奇怪的地方。这在原始数据文件中很常见,但不会带来任何问题。
16.1.2打印文件头及其位置
为让文件头数据更容易理解,将列表中的每个文件头及其位置打印出来:
highs_lows.py
import csv
filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
reader = csv.reader(f)
header_row = next(reader)
for index, column_header in enumerate(header_row):
print(index, column_header)
我们对列表调用了enumerate() (见❶)来获取每个元素的索引及其值。(请注意,我们删除了代码行print(header_row) ,转而显示这个更详细的版本。)输出如下,其中指出了每个文件头的索引:
0 AKDT
1 Max TemperatureF
2 Mean TemperatureF
3 Min TemperatureF
4 Max Dew PointF
5 MeanDew PointF
6 Min DewpointF
7 Max Humidity
8 Mean Humidity
9 Min Humidity
10 Max Sea Level PressureIn
11 Mean Sea Level PressureIn
12 Min Sea Level PressureIn
13 Max VisibilityMiles
14 Mean VisibilityMiles
15 Min VisibilityMiles
16 Max Wind SpeedMPH
17 Mean Wind SpeedMPH
18 Max Gust SpeedMPH
19 PrecipitationIn
20 CloudCover
21 Events
22 WindDirDegrees
从中可知,日期和最高气温分别存储在第0列和第1列。为研究这些数据,我们将处理sitka_weather_07-2014.csv中的每行数据,并提取其中索引为0和1的值。
16.1.3提取并读取数据
知道需要哪些列中的数据后,我们来读取一些数据。首先读取每天的最高气温:
highs_lows.py
import csv
# 从文件中获取最高气温
filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
reader = csv.reader(f)
header_row = next(reader)
highs = []
for row in reader:
highs.append(row[1])
print(highs)
我们创建了一个名为highs 的空列表(见❶),再遍历文件中余下的各行(见❷)。阅读器对象从其停留的地方继续往下读取CSV文件,每次都自动返回当前所处位置的下一行。由于我们已经读取了文件头行,这个循环将从第二行开始——从这行开始包含的是实际数据。每次执行该循环时,我们都将索引1处(第2列)的数据附加到highs 末尾(见
❸)。
下面显示了highs 现在存储的数据:
['64', '71', '64', '59', '69', '62', '61', '55', '57', '61', '57', '59', '57', '61', '64', '61', '59', '63', '60', '57', '69', '63', '62', '59', '57', '57', '61', '59', '61', '61', '66']
我们提取了每天的最高气温,并将它们作为字符串整洁地存储在一个列表中。下面使用int() 将这些字符串转换为数字,让matplotlib能够读取它们: highs_lows.py
import csv
# 从文件中获取最高气温
filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
reader = csv.reader(f)
header_row = next(reader)
highs = []
for row in reader:
high = int(row[1])
highs.append(high)
print(highs)
在❶处,我们将表示气温的字符串转换成了数字,再将其附加到列表末尾。这样,最终的列表将包含以数字表示的每日最高气温:
[64, 71, 64, 59, 69, 62, 61, 55, 57, 61, 57, 59, 57, 61, 64, 61, 59, 63, 60, 57,
69, 63, 62, 59, 57, 57, 61, 59, 61, 61, 66]
下面来对这些数据进行可视化。
16.1.4绘制气温图表
为可视化这些气温数据,我们首先使用matplotlib创建一个显示每日最高气温的简单图形,如下所示:
highs_lows.py
# import csv
# filename = 'sitka_weather_07-2014.csv'
# with open(filename) as f:
# reader = csv.reader(f)
# header_row = next(reader)
# for index, column_header in enumerate(header_row):
# print(index, column_header)
import csv
from matplotlib import pyplot as plt
# 从文件中获取最高气温
filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
reader = csv.reader(f)
header_row = next(reader)
highs = []
for row in reader:
high = int(row[1])
highs.append(high)
print(highs)
# 根据数据绘制图形
fig = plt.figure(dpi=128, figsize=(10, 6))
plt.plot(highs, c='red')
# 设置图形的格式
plt.title("Daily high temperatures, July 2014", fontsize=24)
plt.xlabel('', fontsize=16)
plt.ylabel("temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.show()
我们将最高气温列表传给plot() (见❶),并传递c=‘red’ 以便将数据点绘制为红色(红色显示最高气温,蓝色显示最低气温)。接下来,我们设置了一些其他的格式,如字体大小和标签(见❷),这些都在第15章介绍过。鉴于我们还没有添加日期,因此没有给x 轴添加标签,但plt.xlabel() 确实修改了字体大小,让默认标签更容易看清。图 16-1显示了绘制的图表:一个简单的折线图,显示了阿拉斯加锡特卡2014年7月每天的最高气温。
图16-1 阿拉斯加锡特卡2014年7月每日最高气温折线图
16.1.5模块datetime
下面在图表中添加日期,使其更有用。在天气数据文件中,第一个日期在第二行:
2014-7-1,64,56,50,53,51,48,96,83,58,30.19,--snip--
读取该数据时,获得的是一个字符串,因为我们需要想办法将字符串’2014-7-1’ 转换为一个表示相应日期的对象。为创建一个表示2014年7月1日的对象,可使用模块datetime 中的方法strptime() 。我们在终端会话中看看strptime() 的工作原理:
>>> from datetime import datetime
>>> first_date = datetime.strptime('2014-7-1', '%Y-%m-%d')
>>> print(first_date) 2014-07-01 00:00:00
我们首先导入了模块datetime 中的datetime 类,然后调用方法strptime() ,并将包含所需日期的字符串作为第一个实参。第二个实参告诉Python如何设置日期的格式。在这个示例中,’%Y-’ 让Python将字符串中第一个连字符前面的部分视为四位的年份;’%m-’ 让Python将第二个连字符前面的部分视为表示月份的数字;而’%d’ 让Python将字符串的最后一部分视为月份中的一天(1~31)。
方法strptime() 可接受各种实参,并根据它们来决定如何解读日期。表16-1列出了其中一些这样的实参。表16-1 模块datetime中设置日期和时间格式的实参
实参
含义
%A
星期的名称,如Monday
%B
月份名,如January
%m
用数字表示的月份(01~12)
%d
用数字表示月份中的一天(01~31)
%Y
实参 四位的年份,如2015