# Lesson 7 | 高階資料整理 ## 第一節:變數型態轉換(1) * 延續第六堂課,我們將繼續透過```Pandas```來進行資料整理。 * 請在[這裡](https://linchin.ndmctsgh.edu.tw/data/comorbidity_2.csv)下載一份範例資料 - 這份資料是描述每個人疾病狀況的檔案,我們希望將這份直式資料轉換成橫式資料。 ```python= import pandas as pd data = pd.read_csv("comorbidity_2.csv", encoding = 'CP950') data ``` ![L7-1](https://i.imgur.com/DYozbBM.png) - 我們的目標式轉換成這樣的格式。 ![L7-2](https://i.imgur.com/I86m6uY.png) ## 第一節:變數型態轉換(2) * 還記得我們之前有整理過類似的檔案嗎? * 首先我們需要得到這份資料總共有幾種疾病。 * 不知道同學們還記不記得如何索引欄位資料以及透過```.tolist()```轉換成list。 ```python= print(data.columns) print(data['Disease名稱1']) ``` * 透過```*```可以unzip list,接著搭配之前所教的```set()```。 ```python= disease_list = list(set([*data['Disease名稱1'].tolist(), *data['Disease名稱2'].tolist(), *data['Disease名稱3'].tolist()])) # 這邊用了個比較少用的方式來去除空的資料。 disease_list.remove(' ') print(disease_list) ``` ## 第一節:變數型態轉換(3) * 資料轉換的重點只有一個,那就是先想好目標格式,接著利用迴圈功能把目標資料表填滿。 - 當然,也許能找到一些函式能加速整個流程,但在面對小筆資料時,熟練的使用迴圈能幫忙我們迅速做完資料轉換。 * 現在有了所有疾病的資訊後,我們就能開一個```Dataframe```將資料一個一個填入: * 我們要如何創建新的```Dataframe```呢? * 首先得到要創建的欄位名稱以及行數。我們已經有疾病類別的資訊,再額外加上```ID```即可。 ```python= # "+"號是合併兩個list的小技巧。 columns = ["ID"] + disease_list print(columns) final_data = pd.DataFrame( False, index = data.index, columns = columns ) ``` ```python= final_data ``` * 補充說明,以字典輸入作為輸入也是一種創建```Dataframe```的方式。 ```python= test_data = pd.DataFrame( {"A": [1, 2, 3], "B": [4, 5, 6]} ) print(test_data) ``` ## 第一節:變數型態轉換(4) * 同學們應該已經學會單獨取特定行數或是欄位了。 ```python= # 只取特定行數 print(data[0:5]) # 只取特定欄位 print(data[['Disease名稱1', 'Disease名稱2']]) ``` * 那我們要怎麼同時針對特定行與列取值呢? * 在```Pandas```,我們可以透過```.loc```來取值。 ```python= # .loc是以row names以及欄位名稱來取值。 print(data.loc[0:5, 'Disease名稱1']) print(data.loc[0:2, ['Disease名稱1', 'Disease名稱2']]) # 全部都取的話是使用冒號 print(data.loc[:, 'Disease名稱1']) print(data.loc[0:2, :]) ``` * 回到我們的任務,我們要將直式資料轉換成橫式資料的重點在於,依序將個人的資料取出。 ```python= # 第一個人 i = 0 print(data.loc[i, ['問卷編號']]) print(data.loc[i, ['Disease名稱1', 'Disease名稱2', 'Disease名稱3']]) # 第二個人 i = 1 print(data.loc[i, ['問卷編號']]) print(data.loc[i, ['Disease名稱1', 'Disease名稱2', 'Disease名稱3']]) # 第三個人 i = 2 print(data.loc[i, ['問卷編號']]) print(data.loc[i, ['Disease名稱1', 'Disease名稱2', 'Disease名稱3']]) ``` * 以及可以透過Pandas的```.isin()```來產出一個含有哪些疾病的list。 ```python= # 例如回傳那些行數是'高血壓' i = 2 print(data.loc[i, ['Disease名稱1', 'Disease名稱2', 'Disease名稱3']].isin(["高血壓"])) # 也可以回傳多個變數是不是在前面的資料中 print(data.loc[i, ['Disease名稱1', 'Disease名稱2', 'Disease名稱3']].isin(["C肝", "腎結石"])) ``` * 接著,這樣的程式碼就可以回傳一個人在新的資料表上的疾病結果。 ```python= i = 1 # 這邊使用了「1:」代表索引第一個之後的所有變數。來跳過對於ID的判斷 final_data.columns[1:].isin( data.loc[i, ['Disease名稱1', 'Disease名稱2', 'Disease名稱3']]) ``` * 最後,我們只要把資料填回```final_data```裡面即可。 ```python= i = 3 # 先填ID final_data.loc[i, 'ID'] = data.loc[i, '問卷編號'] # 再填是否有Disease final_data.loc[i, disease_list] = final_data.columns[1:].isin( data.loc[i, ['Disease名稱1', 'Disease名稱2', 'Disease名稱3']] ) ``` ## 練習1: * 剛剛所有程式碼由上而下,已經能夠完成這張表格了,現在請你利用迴圈功能把所有步驟完成。 <details> <summary>解答</summary> - 重點還是在撰寫迴圈! ```python= disease_list = list(set([*data['Disease名稱1'].tolist(), *data['Disease名稱2'].tolist(), *data['Disease名稱3'].tolist()])) disease_list.remove(' ') columns = ["ID"] + disease_list final_data = pd.DataFrame( False, index = data.index, columns = columns ) for i in data.index: final_data.loc[i, 'ID'] = data.loc[i, '問卷編號'] final_data.loc[i, disease_list] = final_data.columns[1:].isin( data.loc[i, ['Disease名稱1', 'Disease名稱2', 'Disease名稱3']] ) ``` </details> ## 第二節:資料轉換概念(1) * 現在我們再試試一份另一份更複雜的資料,請按[這裡](https://linchin.ndmctsgh.edu.tw/data/laboratory_1.csv)下載。 * 這份資料是從某醫院生化檢驗值系統上截取10位病患的各式生化值。 ```python= import pandas as pd data = pd.read_csv("laboratory_1.csv", encoding = 'CP950') data ``` ![L7-3](https://i.imgur.com/jN6gfJ3.png) * 我們希望能把資料轉換成這樣的格式 ![L7-3](https://i.imgur.com/Low3Y0u.png) * 同樣的,請同學們構思一下轉換流程。 ## 第二節:資料轉換概念(2) * 首要,我們可以先取出個人的資料,進行轉換,再將資料表整合起來。 * 所以我們先得到有幾位病人。 ```python= cno_list = list(set(data['PATNUMBER'].tolist())) print(cno_list) ``` * 接著,就可以透過索引得到某一個人的資料。 ```python= # 索引搭配條件判斷是非常常用的技巧,請大家熟悉。 i = 0 cno = cno_list[i] sub_data = data[data['PATNUMBER'] == cno] print(sub_data) ``` ## 第二節:資料轉換概念(3) * 讓我們來一步一步拆解,因為一個人有多個時間點的資料,所以我們要再得到每個病人有多少量測時間。 ```python= time_list = list(set(sub_data['COLLECTIONDATE'].tolist())) # 可以用list的```.sort()```方式稍微排序一下。 time_list.sort() print(time_list) ``` * 搭配雙層迴圈,就可以依序宣告某位病人的病歷號以及他的所有時間點。 ```python= for cno in cno_list: sub_data = data[data['PATNUMBER'] == cno] time_list = list(set(sub_data['COLLECTIONDATE'].tolist())) time_list.sort() for sub_time in time_list: print(cno, sub_time) ``` * 有了個人的生化值量測時間我們就可以取出每個時間點的生化值。 ```python= i = 0 sub_time = time_list[i] # 可以先print(sub_data[sub_data['COLLECTIONDATE'] == sub_time]) lab = sub_data['TESTNAME'][sub_data['COLLECTIONDATE'] == sub_time].tolist() value = sub_data['RESVALUE'][sub_data['COLLECTIONDATE'] == sub_time].tolist() ``` ## 第二節:資料轉換概念(4) * 到這裡,整個程式碼的關鍵幾乎已經完成了,接下來我們需要思考如何填值。 * 我們可以創建一個子```DataFrame```並合併他們。 ```python= lab_list = list(set(data['TESTNAME'].tolist())) print(lab_list) columns = ['PATNUMBER', 'COLLECTIONDATE'] + lab_list sub_final_data = pd.DataFrame(columns = columns) ``` * 最裡層的迴圈應該就會長這樣。 ```python= for k in range(len(lab)): sub_final_data.loc[k, ['PATNUMBER', 'COLLECTIONDATE']] = cno, sub_time sub_final_data.loc[k, lab[k]] = value[k] print(sub_final_data) ``` * 我們可以叫出這些欄位來驗證一下是否填入正確。 ```python= print(lab) print(value) sub_final_data.loc[:, lab] ``` ## 第二節:資料轉換概念(5) * 到這裡,我們已經可以將所有程式碼串起來,我們可以由裡往外撰寫。 * 這是第一個人的第一個時間點的所有生化值結果。 ```python= i = 0 sub_time = time_list[i] lab = sub_data['TESTNAME'][sub_data['COLLECTIONDATE'] == sub_time].tolist() value = sub_data['RESVALUE'][sub_data['COLLECTIONDATE'] == sub_time].tolist() sub_final_data = pd.DataFrame(columns = columns) for k in range(len(lab)): sub_final_data.loc[k, ['PATNUMBER', 'COLLECTIONDATE']] = cno, sub_time sub_final_data.loc[k, lab[k]] = value[k] print(sub_final_data) ``` * 接著是完整的程式碼。 ```python= cno_list = list(set(data['PATNUMBER'].tolist())) lab_list = list(set(data['TESTNAME'].tolist())) columns = ['PATNUMBER', 'COLLECTIONDATE'] + lab_list final_data = pd.DataFrame(columns = columns) for cno in cno_list: sub_data = data[data['PATNUMBER'] == cno] time_list = list(set(sub_data['COLLECTIONDATE'].tolist())) time_list.sort() for sub_time in time_list: lab = sub_data['TESTNAME'][sub_data['COLLECTIONDATE'] == sub_time].tolist() value = sub_data['RESVALUE'][sub_data['COLLECTIONDATE'] == sub_time].tolist() sub_final_data = pd.DataFrame(columns = columns) for k in range(len(lab)): sub_final_data.loc[k, ['PATNUMBER', 'COLLECTIONDATE']] = cno, sub_time sub_final_data.loc[k, lab[k]] = value[k] # 這裡我們要額外用一個特別的函式.concat來合併資料表 final_data = pd.concat([final_data, sub_final_data]) ``` ## 練習2: * 資料前處理可以說是資料分析中最重要的部分,不論是後續的視覺化、統計分析甚至是演算法等數學模型的建立,都需要先把資料處裡成能分析的格式。 * 這邊請同學先幫我驗證這樣的轉換是否錯誤,也順便熟悉```DataFrame```的操作。 * 問題1:請利用索引的方式找出PATNUMBER為691的病人,在"2014/8/21 上午 3:52:00"的鉀離子濃度為多少? <details> <summary>解答</summary> ```python= sub_data = final_data[final_data['PATNUMBER'] == 691] sub_data[sub_data['COLLECTIONDATE'] == '2014/8/21 上午 3:52:00']['K'] ``` ```python= sub_data = data[data['PATNUMBER'] == 691] sub_data[(sub_data['COLLECTIONDATE'] == '2014/8/21 上午 3:52:00') & (sub_data['TESTNAME'] == "K")] ``` </details> * 問題2:請問PATNUMBER為1332的病人,在哪些時間他的Total Cholesterol大於250? <details> <summary>解答</summary> ```python= sub_data = final_data[final_data['PATNUMBER'] == 1332] sub_data[sub_data['Total Cholesterol'] > 250] ``` ```python= sub_data = data[data['PATNUMBER'] == 1332] sub_data[(sub_data['TESTNAME'] == 'Total Cholesterol') & (sub_data['RESVALUE'] > 250)] ``` </details> ## 總結 * 經過多次對於索引的學習,我們應該對資料處理又有了更好的認識, * 加上Python基本的list以及dict的操作,你應該不會害怕各種資料處理的任務了! * 同學在上完這堂課後,應有能力面對更複雜的資料處理任務。