使用Python進行資料整理 – 初探Pandas


李孟諵

資料整理是資料分析和建模前的準備工作,包括資料的讀取、檢核、修改、重新編碼和合併等。市場上有不少專為量化研究所設計的商業軟體,例如:SPSSSASStata等,都能進行這項任務,但共通問題是價格所貲不菲。因此,有些人轉向使用開源軟體,其中,具資料整理功能的代表軟體就是「R」。近年來,隨著資料科學[1]Data Science)一詞廣泛被討論,另一套追求開源精神的程式語言Python,因第三方函式庫[2]libraryPandas日漸成熟,使其在資料整理的功能上也逐漸受到關注。因此,為了使大家更容易了解Pandas在調查資料的運用,本文以一個符合調查資料結構的小資料表,藉由調查資料的整理過程,來介紹所使用的函式以及說明程式碼和輸出結果。

一、Python簡介
Python一個開源、簡潔、易讀的程式語言──由Guido van Rossum創建,於1991年首次發布,設計哲學為「優雅」、「明確」、「簡單」,其直譯式[3]、易擴充和跨平台的特性,加上豐富多元的第三方函式庫和社群,使其在各種程式應用上都能看見它的身影,例如:網頁開發、系統管理、資料處理、科學計算,以及近年熱門的機器學習和深度學習等。在近期的TIOBE指數[4]KDnuggets調查[5]都有不錯的熱門度。

二、Python環境安裝
由於跨平台的特性,WindowsMacLinux系統都能安裝。一般從Python原生環境加上所需的函式庫到整合開發環境(Integrated Development Environment,簡稱IDE[6],對初學者來說安裝就是一個挑戰,因此建議直接下載「Anaconda」(號稱Python資料科學懶人包)來安裝,優點是安裝簡單,只需點選「下一步」即可,缺點是有些函式庫可能用不到,會佔據太多磁碟空間。安裝步驟可參考官網說明,惟補充兩個安裝時會遇到的問題(以Windows為例),第一個是環境變數(PATH environment variable)的選擇(如圖一),主要作用在於透過命令提示字元(cmd.exe)呼叫Python或相關程式時,是否需指引到絕對路徑。Anaconda官方建議可不勾選,而是透過Anaconda NavigatorAnaconda PromptWindows開始鈕來開啟IDE




圖一 選擇是否添加環境變數

另一個問題則是可選擇是否安裝VSCodeVisual Studio Code)(如圖二),這是Anaconda5.1版本開始內含的微軟跨平台IDE;此外,也含部分函式庫需要用到的微軟套件。


圖二 選擇是否安裝VSCode

以上兩個問題都依個人使用習慣自行決定,不論是否設定環境變數或有沒有安裝VSCode都不影響後續的操作。目前Python2.x[7]3.x版,其中一個差異就是Python2ASCII編碼,Python3Unicode編碼,因此,讀取資料時要留意原資料的編碼格式,才不會出現亂碼。至於版本選擇上,除非要沿用2.x版開發的環境,不然建議可直接從3.x版開始。

三、Pandas資料結構
PandasPython的資料分析函式庫,從2009年底開放原始碼,提供簡易使用的資料結構和分析工具,讓分析人員可以快速方便的處理結構化資料。要使用Pandas之前,先瞭解兩個主要資料結構
(一)Series
是一維標籤陣列(array),能夠保存任何資料類型(整數、字符串、浮點數等)。
(二)DataFrame
是一個二維標籤資料結構,可以具有不同類型的行(column),類似Excel的資料表,對於有使用過統計軟體的分析人員應該不陌生。
簡單來說,Series可以想像為一行多列(row)的資料,而DataFrame是多行多列的資料,藉由選擇索引(列標籤)和行(行標籤)的參數來操作資料,就像使用統計軟體透過樣本編號或變項名稱來操作資料。

四、Pandas在資料整理的運用
操作環境為Windows7 64位元之Anaconda3 5.3.1版(內含Python 3.7版和Pandas 0.23.4版),並使用Anaconda內建的Jupyter Notebook來當IDE。相關檔案(問卷、過錄編碼簿、程式碼和資料檔)可於SRDA網站「課程講義」下載。
(一)開啟Jupyter Notebook
Jupyter Notebook是一個開源Web應用程式,藉由互動式輸入和輸出來進行開發。開啟的方式可用命令提示字元呼叫(如圖三)。


圖三 用命令提示字元呼叫Jupyter Notebook


或是Windows程式集可用點選的方式開啟(如圖四)。


圖四 Windows程式集點選

Jupyter Notebook啟動後,點選右上角「New」再點選「Python 3」(如圖五),即可進入編輯頁面。由於本文非Jupyter Notebook操作教學,若想瞭解更多可參考文末的網路資源「政大蔡炎龍老師線上課程」。


圖五 點選右上角Python 3


(二)載入函式庫
IDE開起後,起手式就是利用import來載入函式庫。

# 載入函式庫
import pandas as pd
pd.__version__ #查看版本

說明:為保持程式碼簡潔,會利用「pd」簡寫來代替pandas全名。(以下提到pd就是指pandas

(三)函式(function)介紹
1. read_csv()來讀取csv[8]
Pandas提供幾個(如CSVJSONExcel等)用於將表格型資料讀取為DataFrame的函式。為了完整呈現輸出結果,本文虛擬一個8*10的小資料表,並存成csv檔。

# 讀取csv檔案
df = pd.read_csv("C:/py_pandas/data/SRDA20181107.csv") #請自行依檔案位置調整
df

說明:宣告一個df變數(dfDataFrame簡寫,名稱可自行定義),透過pdread_csv()函式指定路徑和檔案名稱,放入一個8 * 10的資料表(8個欄位,10筆樣本)物件,輸出df可看出讀取的資料樣貌。

【輸出】


id
sex
age
a1
a2
a3
a4
ka4
0
1001
1.0
43
2
1
1
5
打零工
1
1002
2.0
46
3
1
2
3
NaN
2
1003
1.0
28
1
2
3
5
NaN
3
1004
NaN
25
4
2
4
2
NaN
4
1005
2.0
77
2
9
5
1
幫家裡做家庭代工
5
1006
2.0
55
3
3
6
3
NaN
6
1006
3.0
60
2
3
7
5
育嬰假
7
1008
NaN
18
1
4
8
4
NaN
8
1009
1.0
15
2
4
9
4
NaN
9
1010
2.0
65
2
9
10
3
NaN

輸出說明:
1. 第一行09為列標籤(索引值),第一列idka4為行標籤(變項名稱)。
2. python預設起始值是從0開始。
3. sex變項非純數字型態,讀取後會顯示小數位。
4. NaNNot a Number)為預設的遺失資料(missing data)標示。
5. 變項說明與選項數值說明可參考問卷或過錄編碼簿。


2.duplicated()來檢查重複值
就調查資料而言,通常一筆樣本會編列一組受訪者識別碼,所以,可用duplicated()來檢查id變項是否有重複的樣本編號。

# 檢查樣本編號
cd1 = df.duplicated('id')
df.id[cd1] #僅輸出id變項

說明:
1. 將檢查條件放入cd1變數(cdcondition簡寫)。
2. df為資料表物件,透過duplicated()檢查id變項。
3. 輸出id變項的值,並帶入cd1的條件。

【輸出】
6    1006
Name: id, dtype: int64

輸出說明:從輸出結果得知,索引值6有重複值情形,為第二筆id變項為1006的數值。

# 補充:亦可整合成一列程式碼,差別在於有沒有把條件存成物件。(當條件存成物件,可方便隨時調用。)
df.id[df.duplicated('id')]


3.isna()來檢查遺漏值
調查資料可能會出現未填答或漏建的情形,可用isna()來檢查指定變項是否出現遺漏值。本文以sex變項為例。

# 檢查遺漏值/缺失值(missing value/ data)
cd2 = df['sex'].isna()
df.sex[cd2] #僅輸出sex變項

說明:
1. 將檢查條件放入cd2變數。
2. df為資料表物件,指定sex變項透過isna()來檢查。
3. 輸出sex變項的值,並帶入cd2的條件。

【輸出】
3   NaN
7   NaN
Name: sex, dtype: float64

輸出說明:從輸出結果可以知道,sex變項有兩筆NaN,分別為索引值37


4.isin()來檢查特定值
處理調查資料時,需要透過特定值來過濾資料,這時候可以用isin()來檢查指定變項是否包含特定值。

(1)檢查特定樣本編號
duplicated()檢查,已得知樣本編號1006出現重複情形,接著我們可以將1006帶入isin()

# 檢查特定樣本編號
cd3 = df['id'].isin([1006])
df[cd3] #輸出整筆資料

說明:
1. 將檢查條件放入cd3變數。
2. df為資料表物件,指定id變項透過isin()檢查1006值。
3. 輸出df資料表,並帶入cd3的條件。

【輸出1

id
sex
age
a1
a2
a3
a4
ka4
5
1006
2.0
55
3
3
6
3
NaN
6
1006
3.0
60
2
3
7
5
育嬰假

# 補充1:依檢查需求,也可僅輸出id變項。
df.id[cd3] #僅輸出id變項

【輸出2
5    1006
6    1006
Name: id, dtype: int64

輸出說明:id變項有兩筆1006數值的樣本,其索引值分別為56,可從其他變項來判斷是否為同一筆樣本或是鍵入錯誤。

# 補充2:亦可整合成一列程式碼
df[df['id'].isin([1006])]

# 補充3:或是結合其他函式,如duplicated()
df[df['id'].isin([df.id[df.duplicated('id')]])]

補充3說明:列出此段程式碼,主要目的在於表示程式碼撰寫的彈性(可一列、可多列或存成物件),當然,撰寫時還是以易讀好維護為主。

(2)檢查類別變項
簡單來說就是受訪者的特徵或答案可以分門歸類,例如:受訪者性別或詢問意向等。可以透過isin()來檢查該變項中是否出現未定義的數值,例如:sex變項的選項(1)代表男、(2)代表女,所以超出這兩個值就是未定義的數值。

# 檢查類別變項
cd4 = ~df['sex'].isin(['1', '2'])
df[cd4] #輸出整筆資料

說明:
1. 將檢查條件放入cd4變數。
2. df為資料表物件,指定sex變項透過isin()檢查不包含12的值。
(1)特別注意,當在最前方加上「~」符號,即表示為「不」。
(2)只要整行不是全部數字,資料會是文字(string)格式存放,因此特定值需要加上上引號「'」表示文字型。
3. 輸出df資料表,並帶入cd4的條件。

【輸出】

id
sex
age
a1
a2
a3
a4
ka4
3
1004
NaN
25
4
2
4
2
NaN
6
1006
3.0
60
2
3
7
5
育嬰假
7
1008
NaN
18
1
4
8
4
NaN

輸出說明:sex變項有三筆不在12的範圍,分別是一筆3的數值和兩筆NaN

# 補充:亦可一列程式碼表示
df[~df['sex'].isin(['1', '2'])]


(3)題目間關聯性
調查問卷有時會設計跳續答或開放題,檢查時,可將多個條件集合起來。

a. 檢查跳續答
依照問卷設計a1變項回答(4)無法選擇,a2變項要跳答。因此邏輯上,當a1變項回答4a2變項卻不是9(跳答碼);而當a1變項不是回答4a2變項卻回答9。我們可以透過這個邏輯,把錯誤的答案挑出來。

# 檢查跳續答
cd5 = ((df['a1'].isin(['4']) & ~df['a2'].isin(['9'])) |
      (~df['a1'].isin(['4']) & df['a2'].isin(['9'])))
df[cd5] #輸出整筆資料

說明:
1. 將檢查條件放入cd5變數。
2. 布林運算子「|」符號為or的意思;「&」符號為and的意思。
3. 為了易讀性可寫成兩列,第一列程式碼表示,當a1變項為4a2變項不為9;第二列程式碼表示,當a1變項不為4a2變項為9。再將第一和第二列條件透過「()」和「|」放入cd5變數。
4. 輸出df資料表,並帶入cd5的條件。

【輸出】

id
sex
age
a1
a2
a3
a4
ka4
3
1004
NaN
25
4
2
4
2
NaN
4
1005
2.0
77
2
9
5
1
幫家裡做家庭代工
9
1010
2.0
65
2
9
10
3
NaN

輸出說明:從輸出結果可看出有3筆不符問卷跳續答設計,如1004樣本a1變項回答4a2變項的答案卻是2不是9;再如1005樣本a1變項回答2a2變項的答案卻是9而不是其他選項數值。

b. 檢查開放題
依照問卷設計a4變項回答(5)其他,ka4變項要鍵入文字。因此邏輯上,當a4變項回答5ka4變項卻無資料(NaN);而當a4變項不是回答5ka4變項有鍵入文字。我們可以利用這個邏輯挑出錯誤的答案。

# 檢查開放題
cd6 = (((df['a4'] == 5) & (df['ka4'].isna())) |
      ((df['a4'].isin([1, 2, 3, 4])) & (df['ka4'].notna())))
df[['id', 'a4', 'ka4']][cd6] #輸出ida4ka4變項

說明:
1. 將檢查條件放入cd6變數。
2. 比較運算子「==」兩個等號為相等的意思。
3. 第一列程式碼表示,當a4變項為5ka4變項卻沒資料;第二列程式碼表示,當a4變項不為5ka4變項卻不是沒資料。將第一和第二列條件透過「()」和「|」放入cd6變數。
4. 輸出df資料表的ida4ka4變項,並帶入cd6的條件。

【輸出】

id
a4
ka4
2
1003
5
NaN
4
1005
1
幫家裡做家庭代工

輸出說明:從輸出結果可看出有2筆不符開放題設計,如1003樣本a4變項回答5ka4變項卻沒鍵入文字,而1005樣本a4變項回答1ka4變項卻確有鍵入文字。


5.between()來檢查區間值
當變項的數值很多時,不適合用isin()來檢查,可採用between()來檢查區間值,如年齡、身高和收入等連續型變項。

# 檢查連續變項
cd7 = ~df['age'].between(18, 65)
df.age[cd7] #輸出age變項

說明:
1. 將檢查條件放入cd7變數。
2. 檢查age變項值域不在1865範圍內的值。(預設是不包含設定本值)
4. 輸出df資料表的age變項,並帶入cd7的條件。

輸出
4    77
8    15
Name: age, dtype: int64

輸出說明:有兩筆年齡不在1865歲之間,一筆是15歲,另一筆是77歲。


6.loc[]修改資料
當資料檢查完成也確認後,接著進行資料的修改。

(1)利用索引值(index
假設翻閱問卷後,確認樣本編號1007誤鍵為1006,可利用loc[]鎖定索引值並調整指定變項的內容。

# 利用索引值修改資料
df.loc[6, 'id'] = 1007
df.id #輸出id變項

說明:
1. 指定索引值6並將id變項資料改為1007
2. 輸出df資料表的id變項。

輸出
0    1001
1    1002
2    1003
3    1004
4    1005
5    1006
6    1007
7    1008
8    1009
9    1010
Name: id, dtype: int64

輸出說明:索引值6id變項資料已改為1007


(2)利用樣本編號
在翻閱問卷後,得知樣本編號1007sex變項應為2,可利用樣本編號指定特定值並調整指定變項的內容。

# 利用樣本編號修改資料
df.loc[df['id']==1007, 'sex'] = 2
df[['id', 'sex']] #輸出idsex變項

說明:
1. 當樣本編號(id)1007時,sex變項改為2
2. 輸出df資料表的idsex變項。

【輸出】


id
sex
0
1001
1.0
1
1002
2.0
2
1003
1.0
3
1004
NaN
4
1005
2.0
5
1006
2.0
6
1007
2.0
7
1008
NaN
8
1009
1.0
9
1010
2.0

輸出說明:可以看到樣本編號(id)1007sex變項已改為2


7.rename()來更改變項名稱
整理資料時,當需要調整變項名稱時,可用rename()來處理。

# 更改變項名稱
df = df.rename(columns={'ka4':'ka4_o'})
df.columns #輸出columns

說明:
1. 重新命名df資料表的columns,並用字典(dictionary)結構{}大括號符號框起來,冒號前為原名稱,冒號後為新名稱。
2. 輸出df資料表的columns

【輸出】
Index(['id', 'sex', 'age', 'a1', 'a2', 'a3', 'a4', 'ka4_o'], dtype='object')

輸出說明:可以看出原ka4變項名稱已改為ka4_o


8.replace()重新編碼
當需要對某變項的數值重新編碼時,可使用replace()來處理。

(1)同一變項
在原變項進行重新編碼。假設要將原12的數值調整為1,原34的數值調整為2,原9數值維持為9

# 重新編碼:同一變項
df['a2'].replace({1:1, 2:1, 3:2, 4:2, 9:9}, inplace=True) #inplace=True才會寫入
df

說明:
1. 指定df資料表的a2變項進行重新編碼,透過字典結構{}大括號符號框起來,冒號前為原數值,冒號後為新數值,再用逗號(,)區分各數值對應情形。
2. 由於預設不會更改資料,需補上inplace=True參數才會寫入資料表。
3. 輸出df資料表。

輸出

id
sex
age
a1
a2
a3
a4
ka4_o
0
1001
1.0
43
2
1
1
5
打零工
1
1002
2.0
46
3
1
2
3
NaN
2
1003
1.0
28
1
1
3
5
NaN
3
1004
NaN
25
4
1
4
2
NaN
4
1005
2.0
77
2
9
5
1
幫家裡做家庭代工
5
1006
2.0
55
3
2
6
3
NaN
6
1007
2.0
60
2
2
7
5
育嬰假
7
1008
NaN
18
1
2
8
4
NaN
8
1009
1.0
15
2
2
9
4
NaN
9
1010
2.0
65
2
9
10
3
NaN

輸出說明:經過重新編碼,可以看出a2變項已沒有34的數值。

(2)新增變項
當需要對某變項的數值重新編碼,且要保留原變項的資料時,可以先建立一個新變項,再使用replace()來處理。假設a3變項15的數值要調整為1610的數值要調整為2,並放到new_a3變項。

# 重新編碼:新增變項
df['new_a3'] = df['a3'].replace({1:1, 2:1, 3:1, 4:1, 5:1, 6:2, 7:2, 8:2, 9:2, 10:2})
df

說明
1. 指定df資料表建立一個new_a3的變項。
2. 指定df資料表的a3變項進行重新編碼,透過字典結構{}大括號符號框起來,冒號前為原數值,冒號後為新數值,再用逗號區分各數值對應情形。
3. 透過等號帶入new_a3變項。
4. 輸出df資料表。

輸出

id
sex
age
a1
a2
a3
a4
ka4_o
new_a3
0
1001
1.0
43
2
1
1
5
打零工
1
1
1002
2.0
46
3
1
2
3
NaN
1
2
1003
1.0
28
1
1
3
5
NaN
1
3
1004
NaN
25
4
1
4
2
NaN
1
4
1005
2.0
77
2
9
5
1
幫家裡做家庭代工
1
5
1006
2.0
55
3
2
6
3
NaN
2
6
1007
2.0
60
2
2
7
5
育嬰假
2
7
1008
NaN
18
1
2
8
4
NaN
2
8
1009
1.0
15
2
2
9
4
NaN
2
9
1010
2.0
65
2
9
10
3
NaN
2

輸出說明:可以看出df資料表已新增new_a3變項,其中12的數值是來自a3變項重新編碼而來。


以上藉由調查資料的基本整理原理來介紹Pandas的運用,希望能讓大家對於程式語言Python和資料分析函式庫Pandas能有進一步認識。最後,本文只是基礎概念的介紹,對於想了解更多的讀者,提供一些可能會有用的資源:







[1] 是一門利用資料學習知識的學科,基本上需要3個領域:Computer Science(技術能力)、Maths & Statistics(數學能力)和Domain Knowledge(某領域知識)。(取自https://ion.icaew.com/itcounts/b/weblog/posts/theaccountinganddatascienceworldsmeet)
[2] 已包裝好的程式碼,可以直接呼叫取用。可以想像是要組裝一部車子,有廠商提供車輪、引擎和車身的「模組」,不用自己造車輪和引擎!
[3] 程式碼一句一句直接執行,不需經過編譯器先行編譯為機器碼之後再執行。
[4] TIOBE指數是根據網際網路上有經驗的程式人員、課程和第三方廠商的數量,並使用搜尋引擎(如GoogleBingYahoo!、百度)以及WikipediaAmazonYouTube統計出排名。
[5] 知名資料科學網站KDnuggets發佈2018年調查結果,超過2,300人參與投票選擇自己使用的資料科學/機器學習工具。
[6] 輔助程式開發者的應用程式,通常包含了程式語言編輯器、編譯器/直譯器、除錯器、還有圖形使用者界面(Graphical User Interface,簡稱GUI)。
[7] Python 2.7 將於 2020年停止維護。(取自https://www.python.org/dev/peps/pep-0373/
[8] 逗號分隔值(Comma-Separated Values,簡稱CSV)有時也稱為字元分隔值,因為分隔字元也可以不是逗號。是目前常見資料交換格式之一,惟讀取時須留意編碼格式(如ASCIIUTF-8Big5),避免造成亂碼。

留言

張貼留言

這個網誌中的熱門文章

SAS、SPSS、STATA 統計軟體檔案格式轉換介紹

資料整理與檢誤經驗談—以SPSS程式進行邏輯檢查