如何用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 jsonpip 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))

對比一下我們自己繪製的圖像和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))

繪圖正確,證明我們的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))

XML數據讀取和檢視成功。

小結

至此,你已經嘗試了如何把CSV、JSON和XML數據讀入到Pandas數據框,並且做最基本的時間序列可視化展示。

你可能會有以下疑問:

既然CSV文件這麼小巧,Pandas讀取起來也方便,為什麼還要費勁去學那麼難用的JSON和XML數據讀取方法呢?

這是個好問題!

我能想到的,至少有兩個原因。

首先,咱們找到的Quandl平台,全方位提供數據的下載格式,幾乎涵蓋了全部常見數據格式類別。但這只是特例。大多數的開放數據平台,是不提供這麼多種數據格式供你下載的。因此,當你拿到的數據只有JSON或者XML格式時,了解如何讀取它們,就很重要。

其次,JSON或XML附加的那些內容,絕不是無意義的。它們可以幫助你檢查數據的完整性合法性。你甚至還可以自行定義語義標準,以便和他人進行高效的數據交換。

如果你對JSON和XML格式感興趣,希望系統學習,那我推薦你到Stanford Online這個MOOC平台上學習資料庫課程。

祝進步!

討論

你平時從哪些開放數據平台下載過數據?都接觸過哪些數據格式?你用什麼工具來整理和分析它們呢?有沒有比本文更高效的方法?歡迎留言,把你的經驗和思考分享給大家,我們一起交流討論。

如果你對我的文章感興趣,歡迎點贊,並且微信關注和置頂我的公眾號「玉樹芝蘭」(nkwangshuyi)。

如果本文可能對你身邊的親友有幫助,也歡迎你把本文通過微博或朋友圈分享給他們。讓他們一起參與到我們的討論中來。


推薦閱讀:

json 在 Python 爬蟲的應用
XML/HTML/JSON——數據抓取過程中不得不知的幾個概念
一捅到底的架構
Bumpover.js - 牢固而趁手的數據校驗轉換庫

TAG:Python | 数据分析 | JSON |