如何用Python读取开放数据?
当你开始接触丰富多彩的开放数据集时,CSV、JSON和XML等格式名词就会奔涌而来。如何用Python高效地读取它们,为后续的整理和分析做准备呢?本文为你一步步展示过程,你自己也可以动手实践。
需求
人工智能的算法再精妙,离开数据也是“巧妇难为无米之炊”。
数据是宝贵的,开放数据尤其珍贵。无论是公众号、微博还是朋友圈里,许多人一听见“开放数据”、“数据资源”、“数据链接”这些关键词就兴奋不已。
好不容易拿到了梦寐以求的数据链接,你会发现下载下来的这些数据,可能有各种稀奇古怪的格式。
最常见的,是以下几种:
- CSV
- XML
- JSON
你希望自己能调用Python来清理和分析它们,从而完成自己的“数据炼金术”。
第一步,你先得学会如何用Python读取这些开放数据格式。
这篇文章,咱们就用实际的开放数据样例,分别为你介绍如何把CSV、XML和JSON这三种常见的网络开放数据格式读取到Python中,形成结构化数据框,方便你的后续分析操作。
是不是跃跃欲试了?
数据
我们选择的开放数据平台,是Quandl。
Quandl是一个金融和经济数据平台。其中既包括价格不菲的收费数据,也有不少免费开放数据。
你需要在Quandl免费注册一个账户,这样才可以正常访问其免费数据集合。
注册过程,只需要填写上面这个表格。注册完毕后,用新账户和密码登录。
登录后,点击首页上的“Core Financial Data”栏目中的“Search Data”。
你马上就看到让你眼花缭乱的数据集合了。
不要高兴得太早。仔细看数据集合右侧的标签,第一页里基本上都是“Premium”(只限会员),只有付费用户才能使用的。
你不需要自己翻页去查找免费开放数据。点击页面左侧上方的过滤器(Filter)下的“免费”(Free)选项。
这次显示的全都是免费数据了。
这些数据都包含什么内容?如果你感兴趣的话,欢迎自己花点儿时间浏览一下。
咱们使用其中的“Zillow Real Estate Research”,这是一个非常庞大的房地产数据集。
Zillow房地产数据都来自于美国城市。你可以根据自己的爱好,选择感兴趣的城市。我选择的是肯塔基州的莱克星顿(Lexington)市。
为什么不选纽约、洛杉矶,却要选它呢?
因为我在美国访学的时候,周末经常去那里。
我访问的大学坐落在村子里。本地没有华人超市,一些常见的食品和调料都买不到。
要想去华人超市,就得到最近的“大城市”莱克星顿。
从学校到那里地距离,跟天津到北京差不多。
我自己没有买车,公共交通又不方便,一开始很是苦恼。
好在留学生同胞们周末时常要去莱克星顿逛商场。我总是跟着蹭车。
一个半小时开车进城,我们先去真正的中餐馆吃一顿自助午餐,然后去商场。他们逛2个小时左右,我找个咖啡馆或者休息区闭目养神,戴着耳机听罗胖讲故事。
等他们逛完了,我们一起去华人超市采购。
这个有大商场、有正牌中餐馆、有多路公交,甚至还有华人超市的“大城市”当初给我留下了难忘的美好回忆。
就拿它当样例吧。
获取
搜索“lexington ky”,返回的结果还真不少。
我们选择其中的“Zillow Home Value Index (Metro): Home Sales (SA) - Lexington, KY”,点击后可以看到这个数据集的页面。
这是莱克星顿房屋销售价格的中位数(median)在不同时间的记录。
Quandl已经很周到地帮我们用折线图绘制了历史价格信息的变化。选择“TABLE”标签页,我们可以查看原始数据。
下面我们把数据下载到本地。右上方有个Download按钮,我们点击它。
可以看到,Quandl提供了我们4种格式的数据,分别是
- CSV
- Excel
- JSON
- XML
这里咱们先不讲Excel(因为它是微软的专属格式),只依次下载其他3个类别的数据。
我们在对应的数据类别上点击鼠标右键,在弹出的浏览器菜单中选择“链接另存为”,然后存储到本地。
我已经为你下载好了相关的3种数据格式,并且存储在了一个Github项目中。请访问这个链接,下载压缩包后,解压查看。
压缩包里,就是莱克星顿市房地产交易信息的三种不同格式了。从这张图里,可以看到同样的数据内容,csv文件占用空间最小,JSON次之;占空间最大的格式是XML。
数据有了,下面我们准备一下Python编程环境。
环境
我们使用Python集成运行环境Anaconda。
请到这个网址 下载最新版的Anaconda。下拉页面,找到下载位置。根据你目前使用的系统,网站会自动推荐给你适合的版本下载。我使用的是macOS,下载文件格式为pkg。
下载页面区左侧是Python 3.6版,右侧是2.7版。请选择2.7版本。
双击下载后的pkg文件,根据中文提示一步步安装即可。
安装好Anaconda后,我们还需要确保安装几个必要的软件包。
请到你的“终端”(Linux, macOS)或者“命令提示符”(Windows)下面,进入咱们刚刚下载解压后的样例目录。
执行以下命令:
pip install json
pip install bs4
安装完毕后,执行:
jupyter notebook
这样就进入到了Jupyter笔记本环境。我们新建一个Python 2笔记本。
这样就出现了一个空白笔记本。
点击左上角笔记本名称,修改为有意义的笔记本名“demo-python-read-open-data-formats”。
至此,准备工作做完,下面我们就可以开始用Python读取不同格式的数据了。
CSV
我们先从最为简单的CSV格式开始。
所谓CSV,是英文“Comma Separated Values”(逗号分割数值)的简写。
我们先回到Jupyter Notebook的根目录。
打开咱们的样例csv文件,ZILLOW-M550_SALES.csv
来看看。
可以看到,第一行是表头,说明每一列的名称。之后每一行都是数据,分别是日期和对应的售价中位数取值。
每一行的两列数据,都是用逗号来分割的。
我们可以用Excel来打开csv数据,更直观来看看效果。
如图所示,当我们用Excel打开csv数据时,Excel自动将其识别为数据表单。逗号不见了,变成了分割好的两列若干行数据。
下面我们使用Python,将该csv数据文件读入,并且可视化。
读入Pandas工具包。它可以帮助我们处理数据框,是Python数据分析的基础工具。
import pandas as pd
然后,为了让图像可以在Jupyter Notebook上正确显示,我们使用以下语句,允许页内嵌入图像。
%matplotlib inline
下面我们读入csv文件。Pandas对csv数据最为友好,提供了read_csv
命令,可以直接读取csv数据。
df = pd.read_csv("ZILLOW-M550_SALES.csv")
我们把csv数据存储到了数据框变量df。下面显示一下数据读取效果。
df.head()
可以看到,日期和交易价格中位数记录都正确读入。
下面我们编制一个函数,帮我们整理数据框。它主要实现以下功能:
- 把列名变成小写的“date”和“value”;
- 按照时间顺序,排列数据。把最旧的日期和对应的数值放在第一行,最新的日期和对应的数值置于末尾;
- 把时间设置为数据框的索引,这主要是便于后面绘图的时候,横轴正确显示日期数据。
def arrange_time_dataframe(df):
df.columns = ['date', 'value']
df.sort_values(by='date', inplace=True)
df.set_index('date', inplace=True)
return df
下面我们调用这个函数,整理数据框变量df。
df = arrange_time_dataframe(df)
我们展示一下df的前5行。
df.head()
你会看到,日期数据变成了索引,而且按照升序排列。
下面我们该绘图了。数据框工具Pandas给我们提供了非常方便的时间序列图形绘制功能。
为了显示更为美观,我们把图形的长宽比例做了设置。
df.plot(figsize=(16, 6))
png
对比一下我们自己绘制的图像和Quandl的示例图形,是不是一致呢?
JSON
JSON是JavaScript Object Notation(JavaScript对象标记)的缩写,是一种轻量级的数据交换格式。它跟CSV一样,也是文本文件。
我们在Jupyter Notebook中打开下载的JSON文件,检视其内容:
我们需要的数据都在里面,下面我们回到Python笔记本文件ipynb中,尝试读取JSON数据内容。
首先我们读取json工具包。
import json
打开咱们下载的M550_SALES.json
文件,读取数据到变量data。
with open("M550_SALES.json") as f:
data = json.load(f)
为了看得更为直观,咱们把JSON正确缩进后输出。这里我们只展示前面的一些行。
print(json.dumps(data, indent=2))
{
"dataset": {
"dataset_code": "M550_SALES",
"column_names": [
"Date",
"Value"
],
"newest_available_date": "2016-06-30",
"description": "The Zillow Home Value Index is Zillow's estimate of the median market value of home sales (nsa) within the metro of Morehead City, NC. This data is calculated by Zillow Real Estate Research (www.zillow.com/research) using their database of 110 million homes.",
"end_date": "2016-06-30",
"data": [
[
"2016-06-30",
64.0
],
[
"2016-05-31",
163.0
],
可以看到,JSON文件就像是一个大的字典(dictionary)。我们选择其中的某个索引,就能获得对应的数据。
我们选择“dataset”:
data['dataset']
下面是结果的前几行。
{u'collapse': None,
u'column_index': None,
u'column_names': [u'Date', u'Value'],
u'data': [[u'2016-06-30', 64.0],
[u'2016-05-31', 163.0],
[u'2016-04-30', 118.0],
我们关心的数据在“data”下面。继续来:
data['dataset']['data']
还是只展示前几行:
[[u'2016-06-30', 64.0],
[u'2016-05-31', 163.0],
[u'2016-04-30', 118.0],
这不就是我们想要读取的数据吗?
为了和csv数据做出区分,我们这次将数据读取后存储在df1变量。
df1 = pd.DataFrame(data['dataset']['data'])
显示一下前几行:
df1.head()
数据都对,可是列名称怪怪的。
没关系,我们刚才不是编制了整理函数吗?不管多么奇怪的列名称,都可以整理好。
df1 = arrange_time_dataframe(df1)
整理之后,咱们再次调用绘图函数,绘制df1的数据:
df1.plot(figsize=(16, 6))
png
绘图正确,证明我们的JSON数据读取成功。
XML
XML是扩展标记语言(eXtensible Markup Language)的缩写。它看起来有些像我们上网时每天都要用到的HTML源码,但是有区别。它的设计初衷,不是为了展示Web页面,而是为了数据交换。
我们在Jupyter Notebook中打开下载的XML文件。
在页面下方,我们看到了自己感兴趣的数据部分,但是数据是用很多标签来包裹的。
下面我们尝试使用Python来提取和整理XML数据。
首先,我们读入网页分析工具Beautifulsoup。
from bs4 import BeautifulSoup
这是一个非常重要的网页信息提取工具,是Python爬虫编写的基础技能之一。
本文只会用到Beautifulsoup的一些简单命令。所以即便你之前从未接触过Beautifulsoup,也没有关系,跟着先做一遍,获得一些感性认知和经验。后面再系统学习。
我建议的系统学习方法,是到Beautifulsoup的文档页面认真阅读和学习。
如果你阅读英文文档有一些困难,可以看翻译好的中文文档,地址在这里。
然后,我们读入下载好的XML数据,存入变量data。
with open("M550_SALES.xml") as f:
data = f.read()
下面我们用“lxml”工具分析解析data数据,并且存储到soup变量里面。
soup = BeautifulSoup(data, "lxml")
解析之后,我们就可以利用Beautifulsoup的强大搜索功能了。
这里我们观察XML文件:
可以看到,我们关心的日期和交易中位数记录存放在datum标签下。
其中,日期数据的类型为“date”,交易价格中位数的类型为“float”。
我们先来尝试使用Beautifulsoup的find_all
函数,提取所有的日期数据:
dates = soup.find_all('datum', type='date')
我们看看提取结果的前5行:
dates[:5]
[<datum type="date">2016-06-30</datum>,
<datum type="date">2016-05-31</datum>,
<datum type="date">2016-04-30</datum>,
<datum type="date">2016-03-31</datum>,
<datum type="date">2016-02-29</datum>]
很好,数据正确提取出来。问题是还有标签数据在前后,此时我们不需要它们。
我们处理一下。对列表每一项,使用Beautifulsoup的text属性提取内容。
dates = [item.text for item in dates]
再看看这次的提取结果:
dates[:5]
[u'2016-06-30', u'2016-05-31', u'2016-04-30', u'2016-03-31', u'2016-02-29']
好的,没问题了。
下面我们用同样的方式处理交易价格中位数记录:
values= soup.find_all('datum', type='float')
显示一下结果:
values[:5]
[<datum type="float">64.0</datum>,
<datum type="float">163.0</datum>,
<datum type="float">118.0</datum>,
<datum type="float">110.0</datum>,
<datum type="float">83.0</datum>]
这次还是有标签,需要去掉。
注意这里我们希望把结果存储为浮点数,所以除了用text属性提取数值以外,还用float()
函数做了转换。
values = [float(item.text) for item in values]
显示一下前5行:
values[:5]
[64.0, 163.0, 118.0, 110.0, 83.0]
数据被正确转换成了浮点数。
我们手里,分别有了日期和交易价格中位数记录列表。下面我们将其转换成为Pandas数据框,并且存储于df2变量里。
df2 = pd.DataFrame({'dates':dates, 'values':values})
看看df2的前几行:
df2.head()
数据我们有了,下面也用我们的自编函数整理一下:
df2 = arrange_time_dataframe(df2)
然后我们尝试对df2绘图:
df2.plot(figsize=(16, 6))
png
XML数据读取和检视成功。
小结
至此,你已经尝试了如何把CSV、JSON和XML数据读入到Pandas数据框,并且做最基本的时间序列可视化展示。
你可能会有以下疑问:
既然CSV文件这么小巧,Pandas读取起来也方便,为什么还要费劲去学那么难用的JSON和XML数据读取方法呢?
这是个好问题!
我能想到的,至少有两个原因。
首先,咱们找到的Quandl平台,全方位提供数据的下载格式,几乎涵盖了全部常见数据格式类别。但这只是特例。大多数的开放数据平台,是不提供这么多种数据格式供你下载的。因此,当你拿到的数据只有JSON或者XML格式时,了解如何读取它们,就很重要。
其次,JSON或XML附加的那些内容,绝不是无意义的。它们可以帮助你检查数据的完整性和合法性。你甚至还可以自行定义语义标准,以便和他人进行高效的数据交换。
如果你对JSON和XML格式感兴趣,希望系统学习,那我推荐你到Stanford Online这个MOOC平台上学习数据库课程。
祝进步!
讨论
你平时从哪些开放数据平台下载过数据?都接触过哪些数据格式?你用什么工具来整理和分析它们呢?有没有比本文更高效的方法?欢迎留言,把你的经验和思考分享给大家,我们一起交流讨论。
如果你对我的文章感兴趣,欢迎点赞,并且微信关注和置顶我的公众号“玉树芝兰”(nkwangshuyi)。
如果本文可能对你身边的亲友有帮助,也欢迎你把本文通过微博或朋友圈分享给他们。让他们一起参与到我们的讨论中来。