Series 的常用屬性
Series 其實就是 NumPy 的 Array 的加工品,所以 Array 有的屬性, Series 絕大部分都可延用:
import pandas as pds = pd.Series([1, 2, 3])
print(s)
# 0 1
# 1 2
# 2 3
# dtype: int64
print(type(s))
# <class 'pandas.core.series.Series'>
DataFrame
import pandas as pddf = pd.DataFrame([1, 2, 3])
print(df)
# 0
# 0 1
# 1 2
# 2 3
print(type(df))
# <class 'pandas.core.frame.DataFrame'>
Series、DataFrame 與 NdArray 的比較
- DataFrame 代表的是用「資料」的角度去思考程式中的實踐應該長什麼樣子、應該要提供哪些方法,是以資料的觀點出發。
- 那你可以會問既然 Series、DataFrame 都是由陣列所封裝而成的加工品,那為什麼不直接用陣列就好呢?陣列當中所有資料型態必須相同,而 DataFrame 是由 Series 所組成,也就說同一個欄位形態相同,欄位與欄位間可不相同。
D9 使用 Pandas DataFrame 的初始化
建立 Pandas 物件
在 NumPy 中有很多種初始化陣列的方法,但是在 Pandas 當中我們都建議「先直接將資料轉換看看」,可以細分成兩種方法:
- 從原有的物件轉換
- 從外部資料讀取而來
Series
如果資料的樣態是一維的話,例如 … 。直接採用 pd.Series(…) 進行轉換:
import pandas as pds = pd.Series([1,2,3], index=['Amy', 'Bob', 'Tom'])
print(s)
# Amy 1
# Bob 2
# Tom 3
# dtype: int64
從 Array 轉換而來
如果資料的樣態是一維陣列的話,用 pd.DataFrame(…) 進行轉換的話會變成僅有一個欄位的表格:
import pandas as pddf = pd.DataFrame([1, 2, 3], index=['a', 'b', 'c'], columns=['No'])
print(df)
# No
# a 1
# b 2
# c 3
如果資料的樣態是二維陣列的話,用 pd.DataFrame(…) 進行轉換的話會變成多個的表格:
import pandas as pddf = pd.DataFrame([[1, 2, 3], [4, 5, 6]], index=['a', 'b'], columns=['A', 'B', 'C'])
print(df)
# A B C
# a 1 2 3
# b 4 5 6
DataFrame 的來源樣態可以很多元
import pandas as pddf = pd.DataFrame([[1, 2, 3], [4, 5, 6]], index=['a', 'b'], columns=['A', 'B', 'C'])
print(df)
# A B C
# a 1 2 3
# b 4 5 6print(df['B'])
# a 2
# b 5
# Name: B, dtype: int64print(type(df['B']))
# <class 'pandas.core.series.Series'>
D10 Pandas DataFrame 的資料選取
從 DataFrame 選取資料
資料選取是 DataFrame 最重要的操作之一,仰賴於內部的 index 結構,讓我們得以高效的資料選取。在 DataFrame 當中選取資料有幾種方式:
- 利用欄位名稱選取單行資料
- 利用欄位名稱選取多行資料
- 利用列索引位置選取單列/多列資料
- 用 loc, iloc, ix 取得行與列
- 用 iat, at 取得資料
- 根據條件篩選資料(遮罩)
loc函數:通過行索引”Index”中的具體值來取行數據(如取”Index”為”A”的行)
iloc函數:通過行號來取行數據(如取第二行的數據)
合併資料
串連(concat)
pandas 的可以將多個物件的資料合成一個新物件。DataFrame 的串連(concat)可以在任何指定的欄位進行結合,如資料對其後出現遺漏值將會填入 NaN。
使用 concat 串聯參數 axis=0 縱向結合後發現 stock_data1 不存在 high 欄位,stock_data2 不存在 close 欄位,那麼該位子會被填入 NaN。
使用concat串聯參數axis=1橫向結合,會以列索引標籤對齊。
串連(concat)在合併資料時,連結類型預設是外連結(outer join)連集的操作,連結類型可以用 join 參數調整,如 join=’inner’ 表示連結型態為內連結(inner join)交集的操作。
合併(merge)
合併是藉由找出一或多個行或列索引的吻合值,然後將兩資料結合。
以下為針對合併欄位 (key)date 做合併,合併方式為 how=’outer’ 外連結,如除了合併欄位 date 之外還有相同欄位名稱,pandas 將會自動把重複欄位名稱加上 ‘_x’ 代表左邊 DataFrame stock_data1 的重複欄位open,加上 ‘_y’ 代表右邊 DataFrame stock_data2 的重複欄位 open。
pd.merge() 預設連結類型為內連結(inner join),參數 how 可以更改連結類型,
inner : 兩資料集的交集
outer: 兩資料集的聯集
left : 只使用左資料的合併欄位(key)
right : 只使用右資料的合併欄位(key)
合併的另一種方法.join(),利用兩個 DataFrame 的索引標籤(index)進行連結操作,在這裡要注意,除了 date 是索引標籤(index)以外兩資料還有一個 open 欄位名稱重複,因為 join 不像 merge 會自動對於重複欄位產生字尾,所以需要參數 lsuffix、rsuffix 加上指定字尾。
利用欄位名稱選取單行資料
可以使用類似於列表跟字串的索引選取方法,直接使用 […] 的方式挑選出欄位:
import pandas as pddf = pd.DataFrame([[1, 2, 3], [4, 5, 6]], index=['a', 'b'], columns=['A', 'B', 'C'])print(df[['A', 'B']])
print(df[['A', 'C']])
利用列索引位置選取單列/多列資料
除了利用索引的方式之外,在列表也可以用切片取出部分的元素。在 DataFrame 則可以使用切片的方式選出以列為單位的資料:
import pandas as pddf = pd.DataFrame([[1, 2, 3], [4, 5, 6]], index=['a', 'b'], columns=['A', 'B', 'C'])print(df[0:1])
print(df[0:2])
用 loc, iloc, ix 取得行與列
前面兩種方法可以利用行或列的角度來選取資料,不過一次僅能做一個維度的篩選。為了有效的使用 DataFrame 二維的特性,在 Pandas 當中提供了一種座標選取的方法 .loc[…]:
import pandas as pddf = pd.DataFrame([[1, 2, 3], [4, 5, 6]], index=['a', 'b'], columns=['A', 'B', 'C'])print(df.loc['a', 'A']) # 1
print(df.loc['a', ['A', 'B']])
print(df.loc[['a', 'b'], 'A'])
print(df.loc[['a', 'b'], ['A', 'B']])
根據條件篩選資料(遮罩)
D11 Pandas 中的算術運算特性
相同欄位的算術運算
- 在 DataFrame 的算術運算也有「對齊」的特性:
mport pandas as pddf1 = pd.DataFrame([[1, 2, 3]])
df2 = pd.DataFrame([[1, 1, 1]])print(df1 + df2)
# 0 1 2
# 0 2 3 4
- DataFrame 也有廣播的特性:在 DataFrame 的「廣播」的特性也比較嚴格,只有支援常數的廣播:
比較和邏輯運算
比較運算是用來判斷數值之間的比較關係,邏輯運算適用於布林值的組合。在 NumPy 當中,我們會利用比較/邏輯運算所產生的布林陣列作為遮罩的條件,從陣列中篩選出滿足條件的元素。
print(df > 2)
# A B C
# a False False True
# b True True Trueprint(df[df > 2])
# A B C
# a NaN NaN 3
# b 4.0 5.0 6
DataFrame 中的排序
import pandas as pddf = pd.DataFrame({
'col1': ['A', 'a', 'B', 'b'],
'col2': [2, 1, 9, 8],
})
df.sort_values(by=['col1'])
DataFrame 的字串方法
- df.str.findall() :對所有欄位的文字進行 re.findall()
- df.str.replace() :對所有欄位的文字進行取代
- df.str.contains() :對所有欄位的文字進行包含子字串的檢查
- df.str.count() :對所有欄位的文字進行計數操作
- df.str.split() :對所有欄位的文字進行分割操作
統計函式 平均值mean()
- 首先是最常使用到的平均值 mean(),pandas 可針對指定欄位算平均值,如果沒指定會對全部欄位算平均值。
- 如果想要個別計算列或者是行總平均:
統計函式 加總sum() 、個數count()
- 加總 : 計算總和,時常用在計算家庭開銷
- 個數 : 計算個數,時常用在出遊時的點名
統計函式:中位數median()
- 中位數通常使用在有否贏過 50% 的數據,假如薪資中為數為 4 萬,超過 4 萬即為贏過 50% 的人,反之亦然。
- 中位數:通過把所有觀察值高低排序後找出正中間的一個作為中位數。 如果觀察值有偶數個,則中位數不唯一,通常取最中間的兩個數值的平均數作爲中位數。
- 以利用中位數算出各科中位數,如果今天數學考了 60 分超過了中位數的 58 分,我就可以說我數學贏過了全班一半的同學。
統計函式:百分位數quantile()
- 百分位數使用在觀察數據百分比,最常運用到的是升學分數的百分位數。
- 百分位數:將一組數據從小到大排序,並計算相應的累計百分位,則某一百分位所對應數據的值就稱為這一百分位的百分位數。如果百分位數設定在 50% 即為中位數。
統計函式:最大值max()、最小值min()
統計函式:標準差std(),變異數var()
統計函式:相關係數corr()
相關係數 : 皮爾遜積矩相關係數(Pearson product-moment correlation coefficient)用於度量兩個變數X和Y之間的相關程度(線性相依)。在自然科學領域中,該係數廣泛用於度量兩個變數之間的線性相依程度。相關係數的值介於 –1 與 +1 之間,即 –1≤r≤+1。其性質如下:
- 當 r>0 時,表示兩變數正相關,r<0 時,兩變數為負相關,r=0 時,表示兩變數間無線性相關關係。
- 一般可按三級劃分:|r|<0.4 為低度線性相關;0.4≤|r|<0.7 為顯著性相關;0.7≤|r|<1為高度線性相關。
自訂義的行或列函式應用 apply()
D12 Pandas 迭代與重複操作
橫向的資料迭代
如果我們想要對以「筆」為單位的資料迭代的話,最暴力的方法可以這樣做:
import pandas as pddf = pd.DataFrame({
'name': ['Alice', 'Bob'],
'age': [20, 32]
})for i in range(len(df)):
print(df.iloc[i])
第二種方法可以直接用 DataFrame 內建的 iterative 方法:
for d in df.iteritems():
print(d)for d in df.iterrows():
print(d)for d in df.itertuples():
print(d)1.
('name', 0 Alice 1 Bob Name: name, dtype: object)
('age', 0 20 1 32 Name: age, dtype: int64)2.
(0, name Alice age 20 Name: 0, dtype: object)
(1, name Bob age 32 Name: 1, dtype: object)3.
Pandas(Index=0, name='Alice', age=20)
Pandas(Index=1, name='Bob', age=32)
apply
第三種方法是使用 Pandas 當中的 apply 方法,apply 是一種用於逐行或逐列的循環處理方法,常搭配 lambda 匿名函式一起使用:
df.apply(lambda x: x.max() - x.min())
map
另外一種跟 apply 很像的方法叫做 map:
df = pd.DataFrame({
'score': [98, 67, 85],
'age': [20, 32, 28]
})df['age'].map(lambda x: -x)
applymap
在 Pandas 當中,有一種同時 apply 和 map 方法稱為 applymap:
df = pd.DataFrame({
'score': [98, 67, 85],
'age': [20, 32, 28]
})df.applymap(lambda x: -x)
Map、 Apply、Applymap
接著,我們總結一下這三種類似的方法本質上有何差異:
- map:對 series 所有元素作一樣的操作
- apply:對 series 或 dataframe 逐行或逐列做一樣的操作
- applymap:對 dataframe 所有元素做一樣的操作
不建議在 DataFrame 進行迭代操作
如同我們在 NumPy 提過的,不建議在 DataFrame 進行迭代操作。原因是這樣就回到了 Python 列表的操作,無法享受矩陣特性帶來的優勢。
D13 Pandas Dataframe 的新增與刪除
從 DataFrame 中插入或刪除資料
對於一個 DataFrame 可以進行新增或刪除的操作,又可以分為行或是列的方向:
- = 可以用來增加行(欄)
- append() 可以用來新增列(資料)
- del 或 pop() 可以用來刪除行(欄)
- drop() 可以用來刪除列(資料)
= 可以用來增加行(欄)
可以利用指派運算(=)值些產生新的欄位:
df = pd.DataFrame([[1], [2]], columns = ['a'])
df['b'] = pd.Series([3, 4])
append() 可以用來新增列(資料)
df = pd.DataFrame([[1, 2]], columns = ['a', 'b'])
df = df.append(pd.DataFrame([[3, 4]], columns = ['a', 'b']))
但仔細看一下,會發現索引重複了,這邊利用 reset_index() 修正:
df = df.append(pd.DataFrame([[3, 4]], columns = ['a', 'b']))
df = df.reset_index(drop=True)
del 或 pop() 可以用來刪除行(欄)
drop() 可以用來刪除列(資料)
DataFrame 的合併與重組
除了對於一個 DataFrame 的操作之外,我們也會需要 DataFrame 跟 DataFrame 之間的操作,常見可以分為以下幾種:
- 連集(Concat)
- 合併(merge)
- 連接(Join)
- 分組(Group)
連集(Concat)
利用連集(Concat)上下相拼:
one = pd.DataFrame({
'id':[1, 2],
'Name': ['Alex', 'Amy'],
})
two = pd.DataFrame({
'id':[1, 2],
'Name': ['Bob', 'Tom']
})pd.concat([one, two])
有時候會有索引重複的現象,請務必要修正:
one = pd.DataFrame({
'id':[1, 2],
'Name': ['Alex', 'Amy'],
})
two = pd.DataFrame({
'id':[1, 2],
'Name': ['Bob', 'Tom']
})pd.concat([one, two]).reset_index(drop=True)
合併(merge)
利用合併(merge)實現欄位左右相拼:
one = pd.DataFrame({
'id':[1, 2],
'Name': ['Alex', 'Amy'],
})
two = pd.DataFrame({
'id':[1, 2],
'Score': [98, 60]
})pd.merge(one, two, on='id')
連接(Join)
利用連接(Join)實現索引左右相拼:
one = pd.DataFrame({
'Name': ['Alex', 'Amy'],
})
two = pd.DataFrame({
'Score': [98, 60]
})one.join(two)
分組(Group)
另外有一種比較特別的用法稱為是分組(Group),會依照資料內容重新組裝:
df = pd.DataFrame({
'A' : ['foo', 'bar', 'foo', 'bar'],
'B' : ['one', 'one', 'two', 'three'],
'C' : [1,2,3,4],
'D' : [10, 20, 30, 40]
})
df.groupby('A').sum()
df.groupby('A').agg(sum)
df.groupby(['A','B']).sum()
D14 Pandas 的外部資料存取
建立 Pandas 物件
在 NumPy 中有很多種初始化陣列的方法,但是在 Pandas 當中我們都建議「先直接將資料轉換看看」,可以細分成兩種方法:
- 從原有的物件轉換
- 從外部資料讀取而來
認識類別資料
變數的特徵屬於非數值型態。需利用一組的標記、類別、性質或名稱以區別每個基本單位的特徵、屬性。無法以數值表示的統計資料,如航班編號、性別、學歷、旅遊同伴、頭髮顏色、宗教等。類別資料中可以分為兩類順序性與一般性兩種
- 順序性 : 類別之間存在順序性,例如:衣服尺寸[XL,L,M]、長度[短,中,長]
- 一般性 : 類別之間沒有順序關係,例如 : 顏色[黃,綠,藍]、性別[男,女]
大部分的模型都是基於數學運算,字串無法套入數學模型進行運算,在此先對其進行 encoding編碼(將類別資料轉成數字)才能進一步對其做分析。
對於順序性的類別資料,需要有順序性的 encoding 方法,可以使用 sklearn 中的 LabelEncoder()。
對於一般性的類別資料,則不需要有順序的編碼,可以使用 pandas 中的 get_dummies()
認識缺值處理方法與應用函式 定值補值 / 前後補值
df.fillna(number / mean .etc)
# 函式一樣使用 fillna(),我們只需要進一步運用參數method=‘ffill’即可填補前一列數值, method=‘bfill’ 填補後一列數值temp_data.fillna(method='fill')
Missing Value 是什麼?
很常在讀寫資料的過程中,產出 NaN 的型態,那這個東西到底是什麼呢?
NaN 是一種被定義在 NumPy 中的特殊型態,全名為 Not a Number,通常被當成是「空值/缺失值」使用。
缺值資料的處理
缺值是程式或模型中無法正確計算的資料,必須先處理乾淨後才可以進行後面的操作。常見的處理策略如下:
- 直接刪除含有缺失值的資料或欄位
- 利用人工填補遺失值
- 利用常數或通用值填補遺失值
- 利用類似資料/全部資料的統計值值填補遺失值
- 利用統計方法進行補值(內差/回歸)
- 利用機器學習方法進行補值(預測)
D15 用 Pandas 撰寫樞紐分析表(略)
D16 用 Pandas 執行聚合運算(Split-Apply-Combine Strategy)
認識 groupby
在數據分析中時常會分析不同族群的資料,例如,學生分數資料(如表 1),你想分析男生與女生的各科差異,前幾天有教到檢索可以將資料分成男生資料與女生資料,在將各資料算平均值(如圖 2),在這裡有一個函數 goupby 可以一行指令執行以上的邏輯(下圖 3)。
你的 dataframe 變數名稱.groupby(['要分析之行的名稱']).運算函數名稱()
認識 Split-Apply-Combine 策略
以剛剛學生資料來分解一下 groupby 的邏輯過程
- Split:將大的數據集拆成可獨立計算的小數據集(拆成男生、女生資料)
- Apply:獨立計算各個小數據集(成績取平均)
- Combine:將小數據集運算結果合併
將 DataFrame 依照A、B 、C 拆成三個小數據集[split],各自計算總合[Apply],合併結果輸出[Combine]
- 拆分成 A、B 、C 小數據集的方法為 groupby
Groupby 針對多個欄位做分析
Groupby 也可以針對多個欄位做分析,例如,學生成績資料多一欄位 c 班級(class),想對班級以及性別做分類,在 groupby 中加入兩個欄位名稱即可(如下圖),此時 groupby 自動會生成多維度索引(multiple index)
Groupby 針對欄位做多個分析
Groupby也可以針對欄位做多個分析,例如,學生成績資料,想針對性別做成績平均以及標準差的計算,在 groupby 後加入 agg() (如下圖),在 agg 中加入計算的邏輯(mean,std),此時 groupby 自動會生成多維度欄位(multiple columns)
注意 agg() 中要給的是函數名稱而非函數,所以在函數名稱後面不用加 ()。
Groupby 同時針對多個欄位做多個分析
- Groupby 也可以同時針對多個欄位做多個分析,例如,學生成績資料,想針對性別、班級做成績平均以及最高分的計算
- 合併了多欄位以及多分析
D17 Pandas 時間序列資料處理
時間序列資料處理
- 所有資料中只要有時間關係就需要使用到時間序列的資料型態,因為資料之間是有時間關係的,資料之間的時間距離也不盡相同,例如下表,紅框內同樣差一個月,但是相差的天數不同
- 以每個月月底資料來檢視,這組資料並無缺值,但是以日資料來看,就缺了很多筆資料了
- 時間序列的資料非常注重間的間隔
可以運用 resample 函數將年轉成季,如沒有值的填上 nan。
df = pd.Series([1, 2, 3, 4, 5], index=pd.period_range('2021-01-01', freq='Y', periods=5))
df
更改時間頻率如果從年轉成季該怎麼做?
df.resample('Q', convention='start').asfreq()
D18 Pandas 大型資料處理與效能調校
效能調校
接下來有三個方法帶給大家,可以大幅減少程式的執行時間
- 讀取資料型態選最快速的
- 多使用內建函數
- 向量化的資料處理