# 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
```

- 我們的目標式轉換成這樣的格式。

## 第一節:變數型態轉換(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
```

* 我們希望能把資料轉換成這樣的格式

* 同樣的,請同學們構思一下轉換流程。
## 第二節:資料轉換概念(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的操作,你應該不會害怕各種資料處理的任務了!
* 同學在上完這堂課後,應有能力面對更複雜的資料處理任務。