當你開始接觸豐富多彩的開放資料集時,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))
複製程式碼
對比一下我們自己繪製的影像和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)。
如果你對資料科學感興趣,不妨閱讀我的系列教程索引貼《如何高效入門資料科學?》,裡面還有更多的有趣問題及解法。