# Cleaning Data in Python
###### tags: `Datacamp` `python` `Cleaning Data`
>**作者:何彥南**
>Datacamp 課程: [Cleaning Data in Python](https://www.datacamp.com/courses/cleaning-data-in-python)
**注意:**
1. df 為 pandas 的 DataFrame 的縮寫。
2. pd 為 panda 套件的縮寫。
3. 請以官方文件 [panda doc](https://pandas.pydata.org/pandas-docs/stable/) 為主。
4. 注意panda 的版本,有些功能可能在新版無法使用。
5. 程式碼內`#`標記的地方為 output
---
[TOC]
---
# Exploring your data
## [1-1] Diagnose data for cleaning
### 1. Cleaning data
* Prepare data for analysis
* Data almost never comes in clean
* Diagnose your data for problems
### 2. Common data problems
* Inconsistent column names (欄位名不一致)
* Missing data (遺漏值)
* Outliers (極端值)
* Duplicate rows (重複資料)
* Untidy (不整齊)
* Need to process columns (需經過處理的欄位)
* Column types can signal unexpected data values
### 3. Unclean data
> 舉個例來說

* 上表有幾個問題:
* Column name inconsistencies (欄位名不一致)
* Missing data (遺漏值)
* Country names are in French (語言或編碼問題)
### 4. Load your data | read_csv()
> 載入你的資料
#### **pd.read_csv()**: [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html)]
* 常用參數
* **path**(必要):檔案位置。
* header: 指定載入的行名(columns name)。
* header=None 當檔案沒行名時可以使用,以(0、1、2...)代替。
* 預設 header=0 ,代表已第 0 個(index)列為行名。
* names: 更改行名。以 list 匯入。
* usecols: 指定要載入的行。以 list 匯入, 數字(index)或行名都行。
* skiprows: 跳過前面幾行,輸入 int。
* encoding: 讀取的編碼,輸入 string。
```python=
import pandas as pd
df = pd.read_csv('literary_birth_rate.csv')
```
### 5. Visually inspect | head() 、tail()
> 用看的 ,先把看得到的錯誤排除

#### **df.head()** [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.head.html)]
* 前n列,預設5。
#### **df.tail()**: [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.tail.html#pandas.DataFrame.tail)]
* 後n列,預設5。
### 6. Visually inspect(2) | columns、shape、info()

#### **columns**:
* 所有欄的名字(可迭代的)
#### **shape**:
* 表格的規格,輸出:(列,行)。
#### **info()**: [[doc]](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.info.html)
* 表格的整體資訊。
## [1-2] Exploratory data analysis
### 1. Data type of each column
>不通欄位的 type(資料類型)

### 2. Frequency counts: continent | value_counts()
#### **df[column].value_counts()**: [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.value_counts.html)]
* 依照指定行(column),對每個值進行分類計數(count)
* 輸出以分類為index,值為次數
* 注意:
* value_counts().values : 取得結果次數列表(每個分類的次數)
* value_counts().index : 分類列表
* 參數:
* ascending: 預設為降序,輸入bool,True 為升序。
* normalize: 是否使用比例,輸入bool。
* dropna: 丟掉空值。
```python=
df.continent.value_counts(dropna=False)
df['continent'].value_counts(dropna=False)
#[Out]:
'''
AF 49
ASI 47
EUR 36
LAT 24
OCE 6
NAM 2
Name: cont
'''
```
* 在DataFrame 下,df.continent 和 df['continent'] 都是呼叫資料表 (df) 裡的欄位(continent)
* 結果裡面有可能出現 missing 或 NaN 值,這也是我們要注意的地方,後面會在解釋。
### 3. Summary statistics
> 摘要統計
* Numeric columns: 數值化的行列資訊。
* outlier (異常值): 異常值(又稱極端值、離群值) ,我們可以知道他是較高(或低),之後再進一步的去調查。
### 4. Summary statistics: Numeric data | describe()
#### **df.describe()**: [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.describe.html)]
* 可以顯示資料整體的數值化統計量
```python=
df.describe()
#[Out]:
'''
female_literacy population
count 164.000000 1.220000e+02
mean 80.301220 6.345768e+07
std 22.977265 2.605977e+08
min 12.600000 1.035660e+05
25% 66.675000 3.778175e+06
50% 90.200000 9.995450e+06
75% 98.500000 2.642217e+07
max 100.000000 2.313000e+09
'''
```
## [1-3] Visual exploratory data analysis
### 1. Data visualization
* Great way to spot outliers and obvious errors
* More than just looking for patterns
* Plan data cleaning steps
### 2. Bar plots and histograms
* Bar plots(直條圖) for discrete data counts
* Histograms(直方圖) for continuous data counts
* Look at frequencies(頻率)
### 3. Histogram(直方圖)
> 適合展示連續型資料的頻率分布
```python=
df.population.plot('hist')
#[Out]:f<matplotlib.axes._subplots.AxesSubplot at 0x7f78e4abafd0>
import matplotlib.pyplot as plt
plt.show()
```

* 從這邊我們可以看出來,人口在一萬與兩萬那邊有異常值。
### 4. Identifying the error
> 辨識異常(錯誤),我們可以利用 panda 裡面基本的篩選功能去觀察異常值。(接續上面)
```python=
df[df.population > 1000000000]
#[Out]:
'''continent country female_literacy fertility population
0 ASI Chine 90.5 1.769 1.324655e+09
1 ASI Inde 50.8 2.682 1.139965e+09
162 OCE Australia 96.0 1.930 2.313000e+09
'''
```
* 不是所有的異常值都不好,要自己判斷。
* 這邊很明顯,Australia 他的 population(人口) 是錯誤的。
### 5. Box plots(箱型圖)
* 可視覺化基本資料的分布:
* Outliers (異常值)
* Min/max (極值)
* 25th, 50th, 75th percentiles (百分位數)
```python=
df.boxplot(column='population', by='continent')
#[Out]:<matplotlib.axes._subplots.AxesSubplot at 0x7ff5581bb630>
plt.show()
```
* 輸出

* 這邊圈起來的部分我們可以看出異常值,1~1.5 之間有兩個,還有一個在2以上。
### 6. Scatter plots(散佈圖)
* Relationship between 2 numeric variables
* Flag potentially bad data(標記潛在的壞數據)
* Errors not found by looking at 1 variable
---
# Tidying data for analysis
## [2-1] Tidy data(乾淨的資料)
* “Tidy Data” paper by Hadley Wickham, PhD
* Formalize(格式化) the way we describe theshape of data
* Gives us a goal when formatting(格式化) our data
* “Standard way to organize data values within a dataset”
### 1. Principles of tidy data
* Columns represent separate variables(變數)
* Rows represent individual(獨立的) observations(觀測值)
* Observational(觀察) units form tables
### 2. Converting to tidy data

* Better for reporting vs. better for analysis
* Tidy data makes it easier to fix common data problems
* The data problem we are trying to fix:
* Columns containing values, instead of variables
### 3. Melting | melt():
> 將多行(包含相同性質的值)轉成一行類別行(由原本行名組成)和一行值(由原本多行底下的相同值組成),並以補上一行類別變數(表示原本屬於哪行),值則是原本兩行裡的數值。
#### pd.melt(): [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.melt.html)]
* 參數:
* frame: 輸入,dataframe 的形式。
* id_vars: 原資料為基準值的行。
* value_vars: 原資料要合併為一行(分類)的行。
* var_name: 轉換後分類那行的名字。
* value_name: 轉換後值的那行的名字。
```python=
pd.melt(frame=df,id_vars='name',value_vars=['treatment a', 'treatment b'],
var_name='treatment', value_name='result')
#[Out]:
'''
name treatment result
0 Daniel treatment a _
1 John treatment a 12
2 Jane treatment a 24
3 Daniel treatment b 42
4 John treatment b 31
5 Jane treatment b 27
'''
```
* 在變化前,資料已人為基準(所以將其設為 id_vars),記錄每個人兩次治療的數值。
* 某些資料表的型態適合報告時呈現,但卻不適合分析,我們必須知道哪種資料表的形狀較適合我們。
* 在使用 pd.melt() 後,每筆資料代表的是每一次治療,這樣我們在後序的分析上就可以更方便且明確。

## [2-2] Pivoting data
### 1. Pivot: un-melting data | Pivot()
> 將列轉換成行

* Opposite(相反) of melting
* In melting, we turned columns into rows
* **Pivoting: turn unique values into separate columns**
* Analysis friendly shape to reporting friendly shape
* **Violates(違背) tidy data principle:**
* **rows contain observations**
* **Multiple variables stored in the same column**
#### df.Pivot(): [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.pivot.html#pandas.DataFrame.pivot)]
* 參數:
* index: 以哪欄為基準。
* columns: 此行包含類別變數,將類別變為行。
* values: 此行包含參考的值。
```python=
weather_tidy = weather.pivot(index='date',columns='element',values='value')
print(weather_tidy)
#[Out]:
'''element tmax tmin
date
2010-01-30 27.8 14.5
2010-02-02 27.3 14.4
'''
```
### 2. Using pivot when you have duplicate entries
> 當基準(index) 同個類別但是存在多個值(values)時,使用 pivot()會造成以下錯誤

* 這邊以date為基準 2010-02-02 tmin 有個值這樣就無法用pivot()轉換
```python=
import numpy as np
weather2_tidy = weather.pivot(values='value',index='date',columns='element')
#[Out]:
'''---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-9-2962bb23f5a3> in <module>()
1 weather2_tidy = weather2.pivot(values='value',
2 index='date',
----> 3 columns='element')
ValueError: Index contains duplicate entries, cannot reshape
'''
```
### 3. Pivot table | Pivot_table()
> 比起 Pivot() ,Pivot table() 有更詳細的參數,可以處裡在轉換時發生上述重複錯誤時。
#### df.pivot_table(): [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.pivot_table.html#pandas.DataFrame.pivot_table)]
* 參數
* index: 以哪欄為基準。
* columns: 此行包含類別變數,將類別變為行。
* values: 此行包含參考的值。
* aggfunc: 處理方式,function
* Has a parameter that specifies how to deal with duplicate values
* Example: Can aggregate the duplicate values by taking their average
```python=
weather2_tidy = weather.pivot_table(values='value',index='date',
columns='element',aggfunc=np.mean)
#[Out]:
'''element tmax tmin
date
2010-01-30 27.8 14.5
2010-02-02 27.3 15.4
'''
```
* 這邊透過設定 aggfunc= : 這參數,可以告訴 python 該如何處理兩個值。在這裡是用平均(np.mean)。
## [2-3] Beyond melt and pivot
### 1. Melting and parsing | str[ ]
* Another common problem:
* Columns contain multiple bits of information

* 這邊 m014 代表0~14 歲的男人,m1524 代表15~24歲的男人。
```python=
pd.melt(frame=tb, id_vars=['country', 'year'])
#[Out]:
'''
country year variable value
0 AD 2000 m014 0
1 AE 2000 m014 2
2 AF 2000 m014 52
3 AD 2000 m1524 0
4 AE 2000 m1524 4
5 AF 2000 m1524 228
'''
```
* Nothing inherently(本質上) wrong about original data shape
* Not conducive(有利於) for analysis
> 使用str擷取第一個字
* pd[column].str[ ]: [[doc](https://pandas.pydata.org/pandas-docs/stable/user_guide/text.html#indexing-with-str)]
* str[0]為第一個字 (位置0),str[-1]為最後一個字,str[1:3]抓取位置在1~3 (不包括3)的字
```python=
tb_melt['sex'] = tb_melt.variable.str[0]
tb_melt
#[Out]:
'''
country year variable value sex
0 AD 2000 m014 0 m
1 AE 2000 m014 2 m
2 AF 2000 m014 52 m
3 AD 2000 m1524 0 m
4 AE 2000 m1524 4 m
5 AF 2000 m1524 228 m
'''
```
* 這邊我們對variable這行使用str[0],將第一個字分出來一個性別欄(sex),方便於之後的分析。
---
# Combining data for analysis
## [3-1] Concatenating data
### 1. Combining data
* Data may not always come in 1 huge(巨大) file
* 5 million row dataset may be broken into 5 separate datasets
* Easier to store and share
* May have new data for each day
* Important to be able to combine then clean, or vice versa(反之亦然)
* 以下表為範例:

### 2. pandas concat | concat()、loc[ ]、iloc[ ]
> 使用 pd.concat() 合併兩個表
#### pd.concat() [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.concat.html)]
* 參數:
* 必要資料,多個表的list
* ignore_index: 在合併完,重設index
```python=
concatenated = pd.concat([weather_p1, weather_p2])
print(concatenated)
#[Out]:
'''
date element value
0 2010-01-30 tmax 27.8
1 2010-01-30 tmin 14.5
0 2010-02-02 tmax 27.3
1 2010-02-02 tmin 14.4
'''
```
* 這邊我們可以發現,合併後的表 index 會保留原本的值。
* 下面我們使用 ignore_index :
```python=
pd.concat([weather_p1, weather_p2], ignore_index=True)
#[Out]:
'''
date element value
0 2010-01-30 tmax 27.8
1 2010-01-30 tmin 14.5
2 2010-02-02 tmax 27.3
3 2010-02-02 tmin 14.4
'''
```
> 我們可以藉由 panda 裡的 .loc[,] 去指定資料表裡,指定 index 和 colum 的值
* loc[index,column]: 指定標籤 label,可為字串。
* iloc [index,column]: 指定位置,只有數字。
* iloc和loc都是由index的和column 組成,只是用來指定指定位置的的基準不一樣。
```python=
concatenated = concatenated.loc[0, :]
#[Out]:
'''
date element value
0 2010-01-30 tmax 27.8
0 2010-02-02 tmax 27.3
'''
```
* 這邊我們指定所有 index 為0的 row。( : 代表所有的意思)
### 3. Concatenating DataFrames
> 這邊我們將年齡和性別分出來,成另一個表,這樣我們就可以針對性別和年齡做分析。

* 提示: 使用df[column].str[reference]
## [3-2] Finding and concatenating data
### 1. Concatenating many files
> 當我們有許多重複的資料需要處理
* Leverage Python’s features with data cleaning in pandas
* In order to concatenate DataFrames:
* They must be in a list
* Can individually(單獨) load if there are a few datasets
* But what if there are thousands?(不同大小時)
* Solution: glob function to find files based on a pattern(模式)
### 2. Globbing | glob 套件
> glob 模塊: 用於查詢文件路徑
#### Glob 套件 [doc](https://docs.python.org/3/library/glob.html)
* Pattern matching for file names
* Wildcards(萬用字元): *和?
* Any csv file: *.csv
* Any single character: file_?.csv
* **Returns a list of file names**
* **Can use this list to load into separate DataFrames**
> 計畫方法
* Load files from globbing into pandas
* Add the DataFrames into a list
* Concatenate multiple datasets at once
> 利用 glob.glob() ,查找文件。
```python=
import glob
csv_files = glob.glob('*.csv')
print(csv_files)
#[Out]:['file5.csv', 'file2.csv', 'file3.csv', 'file1.csv', 'file4.csv']
```
> 使用迴圈去分別將 csv_files 裡的路徑載入,並添加到 list_data 。
```python=
list_data = []
for filename in csv_files:
data = pd.read_csv(filename)
list_data.append(data)
pd.concat(list_data)
```
## [3-3] Merge data
### 1. Combining data
> 除了 concat 我們還可以使用 merge 去合併資料
* Concatenation is not the only way data can be combined

### 2. Merging data | merge()
>針對指定的欄位值去合併兩個不同的資料表
* Similar to joining tables in SQL
* **Combine disparate datasets based on common columns**

#### pd.merge(): [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.merge.html)]
* 參數:
* left: 左邊的資料表
* right: 右邊的資料表
* on: 必須是兩個資料表都有得行名,也可以設定(label or list),預設為None 。
* left_on: 左邊為基準的行
* right_on: 右邊為基準的行
```python=
pd.merge(left=state_populations, right=state_codes,
on=None, left_on='state', right_on='name')
#[Out]:
'''
state population_2016 name ANSI
0 California 39250017 California CA
1 Texas 27862596 Texas TX
2 Florida 20612439 Florida FL
3 New York 19745289 New York NY
'''
```
### 3. Types of merge
> 不同類型的 merge
* All use the same function
* Only difference is the DataFrames you are merging
> One-to-one: 左右為基準的行,皆只有一筆相對應的資料。

> Many-to-one / one-to-many

* 上面為基準的行 (state) 裡有兩個重複的 New York,在將右邊的表合併時,會自動補上相對應的重複值。

> Many-to-Many
---
# Cleaning data for analysis
## [4-1] Data types
### 1. Data types | dtypes
#### df.dtypes [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.dtypes.html)]
>這邊以下表為例

> 利用 df.dtypes 可以看到每個欄位的資料型態。
```python=
print(df.dtypes)
#[Out]:
'''
name object
sex object
treatment a object
treatment b int64
dtype: object
'''
```
### 2. Converting data types | astype()
> 透過 astype() 去改資料的種類
#### df[colum].astype() [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.astype.html)]
```python=
df['treatment b'] = df['treatment b'].astype(str)
df['sex'] = df['sex'].astype('category')
df.dtypes
#[Out]:
'''
name object
sex category
treatment a object
treatment b object
dtype: object
'''
```
### 3. Categorical data
* Converting categorical data to ‘category’ dtype:
* Can make the DataFrame **smaller in memory**
* Can make them be utilized(利用) by other Python libraries for analysis
### 4. Cleaning bad data
> 數值型的資料,再載入時因為 '-' 被判斷成字串。

> 解決方式
#### pd.to_numeric() [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_numeric.html)]
* 參數:
* errors : {‘ignore’, ‘raise’, ‘coerce’}, default ‘raise’
* If ‘raise’, then invalid parsing will raise an exception
* If ‘coerce’, then invalid parsing will be set as NaN
* If ‘ignore’, then invalid parsing will return the input
```python=
df['treatment a'] = pd.to_numeric(df['treatment a'],errors='coerce')
df.dtypes
#[Out]:
'''
name object
sex category
treatment a float64
treatment b object
dtype: object
'''
```
* errors='coerce' : 可以將無法轉換的值換成NaN
## [4-2] Using regular expressions to clean strings
### 1. String manipulation
* Much of data cleaning involves string manipulation(操作)
* Most of the world’s data is unstructured(非結構) text
* Also have to do string manipulation to make datasets consistent with one another
* Many built-in(內置) and external(外部) libraries
* ‘re’ library for regular expressions
* A formal way of specifying a pattern
* Sequence of characters
* Pattern matching
* Similar to globbing
### 2. Example match

### 3. Using regular expressions | re 套件
> 正規表達式模塊 re ,是一個非常強大的文本解析工具。
#### **Re** [[doc](https://docs.python.org/3/library/re.html)]
```python=
import re
pattern = re.compile('\$\d*\.\d{2}')
result = pattern.match('$17.89')
'''相當於:
result = re.match('\$\d*\.\d{2}', '$17.89')
'''
bool(result)
#[Out]:True
```
* re.compile(): 把正規表達式的模式轉換成正規表達物件(regular expression object)。
* 可以結合 search()或 match() 使用。
* re.search(pattern, string, flags=0): 回傳符合正表達式字串的第一個位置,沒有時回傳None。
* re.match(pattern, string, flags=0) 檢查是否能解析成指定字串。
```python=
import re
recipe = "I need 10 strawberries and 2 apples."
print(re.findall("\d+ [a-z]+", recipe))
```
## [4-3] Using functions to clean data
### 1. Complex cleaning
* Cleaning step requires **multiple steps**
* Extract number from string
* Perform transformation on extracted number
* Python function
### 2. Apply | apply()
#### df.apply() [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.apply.html)]
* 參數:
* axis: 控制對行(0)或列(1)
* 輸出: Series
* 可以一次性的對整個序列(list、series、column)做操作 function。
```python=
print(df)
#[Out]:
'''
treatment a treatment b
Daniel 18 42
John 12 31
Jane 24 27
'''
df.apply(np.mean, axis=0)
#[Out]:
'''
treatment a 18.000000
treatment b 33.333333
dtype: float64
'''
df.apply(np.mean, axis=1)
#[Out]:
'''
Daniel 30.0
John 21.5
Jane 25.5
dtype: float64
'''
```
### 3. Applying functions
> 我們要使用 function、 re 和 apply 做資料清理,以下方表格為例:

> 首先寫一個正規表達式
```python=
import re
from numpy import NaN
pattern = re.compile('^\$\d*\.\d{2}$')
```
> 接著寫一個 function

* bool(): 強制轉布林值
* float(): 強制轉浮點數
* replace(): 將原本的字串替換指定的字串
> 接著在使用apply()結合 function 生成新的列('diff')
```python=
df_subset['diff'] = df_subset.apply(diff_money,axis=1,pattern=pattern)
print(df_subset.head())
#[Out]:
'''
Job # Doc # Borough Initial Cost Total Est. Fee diff
0 121577873 2 MANHATTAN $75000.00 $986.00 74014.0
1 520129502 1 STATEN ISLAND $0.00 $1144.00 -1144.0
2 121601560 1 MANHATTAN $30000.00 $522.50 29477.5
3 121601203 1 MANHATTAN $1500.00 $225.00 1275.0
4 121601338 1 MANHATTAN $19500.00 $389.50 19110.5
'''
```
* 注意: 他是設定 axis=1,也就是對列,因為他心產生的值是參考同一列的其他值。
## [4-4] Duplicate and missing data
### 1. Duplicate data | drop_duplicates()
>重複值
* Can skew results

> 使用 drop_duplicates() 處理
#### df.drop_duplicates() [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.drop_duplicates.html)]
```python=
df = df.drop_duplicates()
print(df)
#[Out]:
'''
name sex treatment a treatment b
0 Daniel male - 42
1 John male 12 31
2 Jane female 24 27
'''
```
### 3. Missing data | dropna()、fillna()
> 缺失值

* 處理方法:
* Leave as-is (不處理)
* Drop them (全丟掉)
* Fill missing value (填補)
> 使用 info() 可清楚的知道,每行有多少缺失值
```python=
tips_nan.info()
#[Out]:
'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244 entries, 0 to 243
Data columns (total 7 columns):
total_bill 202 non-null float64
tip 220 non-null float64
sex 234 non-null object
smoker 229 non-null object
day 243 non-null object
time 227 non-null object
size 231 non-null float64
dtypes: float64(3), object(4)
memory usage: 13.4+ KB
None
'''
```
> 方法一 : 使用 dropna() 處理缺失值
#### df.dropna() [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.dropna.html)]
```python=
tips_dropped = tips_nan.dropna()
tips_dropped.info()
#[Out]:
'''
<class 'pandas.core.frame.DataFrame'>
Int64Index: 147 entries, 0 to 243
Data columns (total 7 columns):
total_bill 147 non-null float64
tip 147 non-null float64
sex 147 non-null object
smoker 147 non-null object
day 147 non-null object
time 147 non-null object
size 147 non-null float64
dtypes: float64(3), object(4)
memory usage: 9.2+ KB
'''
```
> 方法二: 使用 fillna() 填補缺失值
#### df[columns].fillna() [[doc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.fillna.html)]
* Fill with provided value
* Use a summary statistic(描述統計)
```python=
tips_nan['sex'] = tips_nan['sex'].fillna('missing')
tips_nan[['total_bill', 'size']] = tips_nan[['total_bill','size']].fillna(0)
tips_nan.info()
#[Out]:
'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244 entries, 0 to 243
Data columns (total 7 columns):
total_bill 244 non-null float64
tip 220 non-null float64
sex 244 non-null object
smoker 229 non-null object
day 243 non-null object
time 227 non-null object
size 244 non-null float64
dtypes: float64(3), object(4)
memory usage: 13.4+ KB
'''
```
* 注意:在 panda 裡面可以使用雙中括號來呼叫多行,像上面的 tips_nan[['total_bill', 'size']]
> 方法三: 使用 test statistic(檢定統計量) 填補
* Careful when using test statistics to fill
* Have to make sure the value you are filling in makes sense(能說的通)
* Median(中位數) is a better statistic in the presence of outliers(百分比的極端值)
```python=
mean_value = tips_nan['tip'].mean()
print(mean_value)
#[Out]:2.964681818181819
tips_nan['tip'] = tips_nan['tip'].fillna(mean_value)
tips_nan.info()
#[Out]:
'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244 entries, 0 to 243
Data columns (total 7 columns):
total_bill 244 non-null float64
tip 244 non-null float64
sex 244 non-null object
smoker 229 non-null object
day 243 non-null object
time 227 non-null object
size 244 non-null float64
dtypes: float64(3), object(4)
memory usage: 13.4+ KB
'''
```
## [4-5] Testing with asserts
### 1. Assert statements
#### assert 方法 [[doc](https://docs.python.org/3/reference/simple_stmts.html#grammar-token-assert-stmt)]
> Assert(斷言) : 陳述在程式中安插除錯用的斷言(Assertion)檢查時很方便的一個方式。
* Programmatically(編程) vs visually checking
* If we drop or fill NaNs, we expect 0 missing values
* We can write an assert statement to verify(驗證) this
* We can detect early warnings and errors
* This gives us confidence that our code is running correctly
```python=
assert 1 == 1
assert 1 == 2
#[Out]:
'''
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-65-a810b3a4aded> in <module>()
----> 1 assert 1 == 2
AssertionError:
'''
```
### 2. Google stock data

>Test column
```python=
assert google.Close.notnull().all()
#[Out]:
'''
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-49-eec77130a77f> in <module>()
----> 1 assert google.Close.notnull().all()
AssertionError:
'''
```
* 這邊我們用 notnull() 去測試 Close 這行裡每個值,是否有空值。
* all() 則是將結果合起來。
* 最後用 assert 去測試有無問題。
```python=
google_0 = google.fillna(value=0)
assert google_0.Close.notnull().all()
```