Pandas的介紹與基本使用
1、什麼是Pandas
當大家談論到資料分析時,提及最多的語言就是Python和SQL,而Python之所以適合做資料分析,就是因為他有很多強大的第三方庫來協助,pandas就是其中之一,它是基於Numpy構建的,正因pandas的出現,讓Python語言也成為使用最廣泛而且強大的資料分析環境之一。如果說沒有pandas的出現,目前的金融資料分析領域還應該是R語言的天下。
2、Pandas能幹什麼
Pandas的主要功能:
- 具備對應其功能的資料結構DataFrame,Series
- 整合時間序列功能
- 提供豐富的數學運算和操作
- 靈活處理缺失資料
- .....
以上就是pandas能完成的一些基礎操作,當然並不完全,下面就來看看pandas到底是怎麼用的。
3、怎麼用Pandas
安裝方法:
pip install pandas
引用方法:
import pandas as pd
3.1、Series
Series是一種類似於一維陣列的物件,由一組資料和一組與之相關的資料標籤(索引)組成。在資料分析的過程中非常常用。
3.1.1、建立方法
第一種: pd.Series([4,5,6,7,8]) 執行結果: 0 4 1 5 2 6 3 7 4 8 dtype: int64 # 將陣列索引以及陣列的值打印出來,索引在左,值在右,由於沒有為資料指定索引,於是會自動建立一個0到N-1(N為資料的長度)的整數型索引,取值的時候可以通過索引取值,跟之前學過的陣列和列表一樣 ----------------------------------------------- 第二種: pd.Series([4,5,6,7,8],index=['a','b','c','d','e']) 執行結果: a 4 b 5 c 6 d 7 e 8 dtype: int64 # 自定義索引,index是一個索引列表,裡面包含的是字串,依然可以通過預設索引取值。 ----------------------------------------------- 第三種: pd.Series({"a":1,"b":2}) 執行結果: a 1 b 2 dtype: int64 # 指定索引 ----------------------------------------------- 第四種: pd.Series(0,index=['a','b','c']) 執行結果: a 0 b 0 c 0 dtype: int64 # 建立一個值都是0的陣列 -----------------------------------------------
對於Series,其實我們可以認為它是一個長度固定且有序的字典,因為它的索引和資料是按位置進行匹配的,像我們會使用字典的上下文,就肯定也會使用Series
3.1.2、缺失資料
- dropna() # 過濾掉值為NaN的行
- fillna() # 填充缺失資料
- isnull() # 返回布林陣列,缺失值對應為True
- notnull() # 返回布林陣列,缺失值對應為False
# 第一步,建立一個字典,通過Series方式建立一個Series物件 st = {"sean":18,"yang":19,"bella":20,"cloud":21} obj = pd.Series(st) obj 執行結果: sean 18 yang 19 bella 20 cloud 21 dtype: int64 ------------------------------------------ # 第二步 a = {'sean','yang','cloud','rocky'} # 定義一個索引變數 ------------------------------------------ #第三步 obj1 = pd.Series(st,index=a) obj1 # 將第二步定義的a變數作為索引傳入 # 執行結果: rocky NaN cloud 21.0 sean 18.0 yang 19.0 dtype: float64 # 因為rocky沒有出現在st的鍵中,所以返回的是缺失值
通過上面的程式碼演示,對於缺失值已經有了一個簡單的瞭解,接下來就來看看如何判斷缺失值
1、
obj1.isnull() # 是缺失值返回Ture
執行結果:
rocky True
cloud False
sean False
yang False
dtype: bool
2、
obj1.notnull() # 不是缺失值返回Ture
執行結果:
rocky False
cloud True
sean True
yang True
dtype: bool
3、過濾缺失值 # 布林型索引
obj1[obj1.notnull()]
執行結果:
cloud 21.0
yang 19.0
sean 18.0
dtype: float64
3.1.3、Series特性
import numpy as np
import pandas as pd
- 從ndarray建立Series:Series(arr)
arr = np.arange(10)
sr = pd.Series(arr) # ndarray建立Series
- 與標量(數字)進行運算:sr * 2
srx = sr * 2 # 與標量(數字)進行運算
- 兩個Series運算
sr * srx # 兩個Series運算
- 布林值過濾:sr[sr>0]
sr[sr>3] # 布林值過濾
- 統計函式:mean()、sum()、cumsum()
# 統計函式
sr.mean()
sr.sum()
sr.cumsum()
3.1.4、支援字典的特性
- 從字典建立Series:Series(dic),
dic = {"A":1,"B":2,"C":3,"D":4,"E":5}
# 字典建立Series
dic_arr = pd.Series(dic)
- In運算:'a'in sr、for x in sr
"A" in dic_arr
------------------------------
for i in dic_arr:
print(i)
- 鍵索引:sr['a'],sr[['a','b','d']]
dic_arr[['A','B']] # 鍵索引
- 鍵切片:sr['a':'c']
dic_arr['A':'C'] # 鍵切片
- 其他函式:get('a',default=0)等
dic_arr.get("A",default=0)
3.1.5、整數索引
pandas當中的整數索引物件可能會讓初次接觸它的人很懵逼,接下來通過程式碼演示:
sr = pd.Series(np.arange(10))
sr1 = sr[3:].copy()
sr1
執行結果:
3 3
4 4
5 5
6 6
7 7
8 8
9 9
dtype: int32
# 到這裡會發現很正常,一點問題都沒有,可是當使用整數索引取值的時候就會出現問題了。因為在pandas當中使用整數索引取值是優先以標籤解釋的,而不是下標
sr1[1]
解決方法:
- loc屬性 # 以標籤解釋
- iloc屬性 # 以下標解釋
sr1.iloc[1] # 以下標解釋
sr1.loc[3] # 以標籤解釋
3.1.6、Series資料對齊
pandas在運算時,會按索引進行對齊然後計算。如果存在不同的索引,則結果的索引是兩個運算元索引的並集。
sr1 = pd.Series([12,23,34], index=['c','a','d'])
sr2 = pd.Series([11,20,10], index=['d','c','a',])
sr1 + sr2
執行結果:
a 33
c 32
d 45
dtype: int64
# 可以通過這種索引對齊直接將兩個Series物件進行運算
sr3 = pd.Series([11,20,10,14], index=['d','c','a','b'])
sr1 + sr3
執行結果:
a 33.0
b NaN
c 32.0
d 45.0
dtype: float64
# sr1 和 sr3的索引不一致,所以最終的執行會發現b索引對應的值無法運算,就返回了NaN,一個缺失值
將兩個Series物件相加時將缺失值設為0:
sr1 = pd.Series([12,23,34], index=['c','a','d'])
sr3 = pd.Series([11,20,10,14], index=['d','c','a','b'])
sr1.add(sr3,fill_value=0)
執行結果:
a 33.0
b 14.0
c 32.0
d 45.0
dtype: float64
# 將缺失值設為0,所以最後算出來b索引對應的結果為14
靈活的算術方法:add,sub,div,mul
到這裡可能就會說了pandas就這麼簡單嗎,那我們接下來一起看看這個二維陣列DataFraeme
3.2、DataFrame
DataFrame是一個表格型的資料結構,相當於是一個二維陣列,含有一組有序的列。他可以被看做是由Series組成的字典,並且共用一個索引。接下來就一起來見識見識DataFrame陣列的厲害吧!!!
3.2.1、建立方式
建立一個DataFrame陣列可以有多種方式,其中最為常用的方式就是利用包含等長度列表或Numpy陣列的字典來形成DataFrame:
第一種:
pd.DataFrame({'one':[1,2,3,4],'two':[4,3,2,1]})
# 產生的DataFrame會自動為Series分配所索引,並且列會按照排序的順序排列
執行結果:
one two
0 1 4
1 2 3
2 3 2
3 4 1
> 指定列
可以通過columns引數指定順序排列
data = pd.DataFrame({'one':[1,2,3,4],'two':[4,3,2,1]})
pd.DataFrame(data,columns=['one','two'])
# 列印結果會按照columns引數指定順序
第二種:
pd.DataFrame({'one':pd.Series([1,2,3],index=['a','b','c']),'two':pd.Series([1,2,3],index=['b','a','c'])})
執行結果:
one two
a 1 2
b 2 1
c 3 3
以上建立方法簡單瞭解就可以,因為在實際應用當中更多是讀資料,不需要自己手動建立
3.2.2、檢視資料
常用屬性和方法:
- index 獲取行索引
- columns 獲取列索引
- T 轉置
- values 獲取值索引
- describe 獲取快速統計
one two
a 1 2
b 2 1
c 3 3
# 這樣一個數組df
---------------------------------------------------------------------------
df.index
> Index(['a', 'b', 'c'], dtype='object')
---------------------------------------------------------------------------
df.columns
> Index(['one', 'two'], dtype='object')
--------------------------------------------------------------------------
df.T
> a b c
one 1 2 3
two 2 1 3
-------------------------------------------------------------------------
df.values
> array([[1, 2],
[2, 1],
[3, 3]], dtype=int64)
------------------------------------------------------------------------
df.describe()
> one two
count 3.0 3.0 # 資料統計
mean 2.0 2.0 # 平均值
std 1.0 1.0 # 標準差
min 1.0 1.0 # 最小值
25% 1.5 1.5 # 四分之一均值
50% 2.0 2.0 # 二分之一均值
75% 2.5 2.5 # 四分之三均值
max 3.0 3.0 # 最大值
3.2.3、索引和切片
- DataFrame有行索引和列索引。
- DataFrame同樣可以通過標籤和位置兩種方法進行索引和切片。
DataFrame使用索引切片:
- 方法1:兩個中括號,無論是先取行還是先取列都可以。
import tushare as ts
data = ts.get_k_data("000001")
data['open'][:10] # 先取列再取行
data[:10]['open'] # 先取行再取列
- 方法2(推薦):使用loc/iloc屬性,一箇中括號,逗號隔開,先取行再取列。
- loc屬性:解釋為標籤
- iloc屬性:解釋為下標
data.loc[:10,"open":"low"] # 用標籤取值
data.iloc[:10,1:5] # 用下標取值
- 向DataFrame物件中寫入值時只使用方法2
- 行/列索引部分可以是常規索引、切片、布林值索引、花式索引任意搭配。(注意:兩部分都是花式索引時結果可能與預料的不同)
3.3、時間物件處理
處理時間物件可能是我們在進行資料分析的過程當中最常見的,我們會遇到各種格式的時間序列,也需要處理各種格式的時間序列,但是一定不能對這些資料懵逼,我們需要找到最合適的招式來處理這些時間。接下來就一起來看吧!!!
3.3.1、時間序列型別
- 時間戳:特定時刻
- 固定時期:如2019年1月
- 時間間隔:起始時間-結束時間
3.3.2、Python庫:datatime
- date、datetime、timedelta
import datetime
# datetime.date() # date物件
today = datetime.date.today() # 獲取當天日期,返回date物件
t1 = datetime.date(2019,4,18) # 設定時間格式為datetime.date
# datetime.datetime() # datetime物件
now = datetime.datetime.now() # 獲取當天日期,返回datetime物件
t2 = datetime.datetime(2019,4,18) # 設定時間格式為datetime.datetime
# datetime.timedelta() # 時間差
today = datetime.datetime.today()
yestoday = today - datetime.timedelta(1) # 以時間做運算
print(today,yestoday)
- dt.strftime()
# 將時間格式轉換為字串
today.strftime("%Y-%m-%d")
yestoday.strftime("%Y-%m-%d")
- strptime()
# 將日期字串轉成datetime時間格式,第二個引數由時間格式決定
datetime.datetime.strptime('2019-04-18','%Y-%m-%d')
3.3.3、靈活處理時間物件:dateutil包
- dateutil.parser.parse()
import dateutil
# 將字串格式的日期轉換為datetime物件
date = '2019 Jan 2nd'
t3 = dateutil.parser.parse(date)
3.3.4、成組處理時間物件:pandas
- pd.to_datetime(['2018-01-01', '2019-02-02'])
將字串轉換為為時間物件
from datetime import datetime
import pandas as pd
date1 = datetime(2019, 4, 18, 12, 24, 30)
date2 = '2019-04-18'
t4 = pd.to_datetime(date1)
t5 = pd.to_datetime(date2)
t6 = pd.to_datetime([date1,date2])
t4,t5,t6 # 轉換單個時間資料是返回Timestamp物件,轉換多個時間資料返回DatetimeIndex物件
>
"""
(Timestamp('2019-04-18 12:24:30'),
Timestamp('2019-04-18 00:00:00'),
DatetimeIndex(['2019-04-18 12:24:30', '2019-04-18 00:00:00'], dtype='datetime64[ns]', freq=None))
"""
將索引設定為時間序列
ind = pd.to_datetime(['2018-03-01','2019 Feb 3','08/12-/019'])
sr = pd.Series([1,2,3],index=ind)
# 補充:
"""
pd.to_datetime(['2018-03-01','2019 Feb 3','08/12-/019']).to_pydatetime()
> array([datetime.datetime(2018, 3, 1, 0, 0),
datetime.datetime(2019, 2, 3, 0, 0),
datetime.datetime(2019, 8, 12, 0, 0)], dtype=object)
# 通過to_pydatetime()方法將其轉換為ndarray陣列
"""
3.3.5、產生時間物件陣列:data_range
- start 開始時間
- end 結束時間
- periods 時間長度
- freq 時間頻率,預設為'D',可選H(our),W(eek),B(usiness),S(emi-)M(onth),(min)T(es), S(econd), A(year),…
pd.date_range("2019-1-1","2019-2-2",freq="D")
> DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04',
'2019-01-05', '2019-01-06', '2019-01-07', '2019-01-08',
'2019-01-09', '2019-01-10', '2019-01-11', '2019-01-12',
'2019-01-13', '2019-01-14', '2019-01-15', '2019-01-16',
'2019-01-17', '2019-01-18', '2019-01-19', '2019-01-20',
'2019-01-21', '2019-01-22', '2019-01-23', '2019-01-24',
'2019-01-25', '2019-01-26', '2019-01-27', '2019-01-28',
'2019-01-29', '2019-01-30', '2019-01-31', '2019-02-01',
'2019-02-02'],
dtype='datetime64[ns]', freq='D')
3.3.6、時間序列
時間序列就是以時間物件為索引的Series或DataFrame。datetime物件作為索引時是儲存在DatetimeIndex物件中的。
# 轉換時間索引
dt = pd.date_range("2019-01-01","2019-02-02")
# 生成一個帶有時間資料的DataFrame陣列
a = pd.DataFrame({"num":pd.Series(random.randint(-100,100) for _ in range(30)),"date":dt})
# 通過index修改索引
a.index = pd.to_datetime(a["date"])
特殊功能:
- 傳入“年”或“年月”作為切片方式
- 傳入日期範圍作為切片方式
- 豐富的函式支援:resample(), strftime(), ……
a.resample("3D").mean() # 計算每三天的均值
a.resample("3D").sum() # 計算每三天的和
...
時間序列的處理在資料分析當中非常重要,但是有時候時間的格式不一致又會讓人非常煩躁,只要把以上祕籍都學會就可以把時間序列製得服服帖帖。
3.4、資料分組和聚合
在資料分析當中,我們有時需要將資料拆分,然後在每一個特定的組裡進行運算,這些操作通常也是資料分析工作中的重要環節。
分組聚合相對來說也是一個稍微難理解的點,需要各位有一定的功力來學習.
3.4.1、分組(GroupBY機制)
pandas物件(無論Series、DataFrame還是其他的什麼)當中的資料會根據提供的一個或者多個鍵被拆分為多組,拆分操作實在物件的特定軸上執行的。就比如DataFrame可以在他的行上或者列上進行分組,然後將一個函式應用到各個分組上併產生一個新的值。最後將所有的執行結果合併到最終的結果物件中。
分組鍵可以是多種樣式,並且鍵不一定是完全相同的型別:
- 列表或者陣列,長度要與待分組的軸一樣
- 表示DataFrame某個列名的值。
- 字典或Series,給出待分組軸上的值與分組名之間的對應關係
- 函式,用於處理軸索引或者索引中的各個標籤
後三種只是快捷方式,最終仍然是為了產生一組用於拆分物件的值。
首先,通過一個很簡單的DataFrame陣列嘗試一下:
df = pd.DataFrame({'key1':['x','x','y','y','x',
'key2':['one','two','one',',two','one'],
'data1':np.random.randn(5),
'data2':np.random.randn(5)})
df
> key1 key2 data1 data2
0 x one 0.951762 1.632336
1 x two -0.369843 0.602261
2 y one 1.512005 1.331759
3 y two 1.383214 1.025692
4 x one -0.475737 -1.182826
訪問data1,並根據key1呼叫groupby:
f1 = df['data1'].groupby(df['key1'])
f1
> <pandas.core.groupby.groupby.SeriesGroupBy object at 0x00000275906596D8>
上述執行是沒有進行任何計算的,但是我們想要的中間資料已經拿到了,接下來,就可以呼叫groupby進行任何計算
f1.mean() # 呼叫mean函式求出平均值
> key1
x 0.106183
y 2.895220
Name: data1, dtype: float64
以上資料經過分組鍵(一個Series陣列)進行了聚合,產生了一個新的Series,索引就是key1
列中的唯一值。這些索引的名稱就為key1
。接下來就嘗試一次將多個數組的列表傳進來
f2 = df['data1'].groupby([df['key1'],df['key2']])
f2.mean()
> key1 key2
x one 0.083878
two 0.872437
y one -0.665888
two -0.144310
Name: data1, dtype: float64
傳入多個數據之後會發現,得到的資料具有一個層次化的索引,key1對應的x\y;key2對應的one\two.
f2.mean().unstack() # 通過unstack方法就可以讓索引不堆疊在一起了
> key2 one two
key1
x 0.083878 0.872437
y -0.665888 -0.144310
補充:
- 1、分組鍵可以是任意長度的陣列
- 2、分組時,對於不是陣列資料的列會從結果中排除,例如key1、key2這樣的列
- 3、GroupBy的size方法,返回一個含有分組大小的Series
# 以上面的f2測試
f2.size()
> key1 key2
x one 2
two 1
y one 1
two 1
Name: data1, dtype: int64
到這跟著我上面的步驟一步一步的分析,會發現還是很簡單的,但是一定不能幹看,還要自己下手練,只有多練才能融匯貫通!!!
3.4.2、聚合(組內應用某個函式)
聚合是指任何能夠從陣列產生標量值的資料轉換過程。剛才上面的操作會發現使用GroupBy並不會直接得到一個顯性的結果,而是一箇中間資料,可以通過執行類似mean、count、min等計算得出結果,常見的還有一些:
函式名 | 描述 |
---|---|
sum | 非NA值的和 |
median | 非NA值的算術中位數 |
std、var | 無偏(分母為n-1)標準差和方差 |
prod | 非NA值的積 |
first、last | 第一個和最後一個非NA值 |
自定義聚合函式
不僅可以使用這些常用的聚合運算,還可以自己自定義。
# 使用自定義的聚合函式,需要將其傳入aggregate或者agg方法當中
def peak_to_peak(arr):
return arr.max() - arr.min()
f1.aggregate(peak_to_peak)
執行結果:
key1
x 3.378482
y 1.951752
Name: data1, dtype: float64
多函式聚合:
f1.agg(['mean','std'])
執行結果:
mean std
key1
x -0.856065 0.554386
y -0.412916 0.214939
最終得到的列就會以相應的函式命名生成一個DataFrame陣列
以上我們是可以通過agg或者是aggregate來實現多函式聚合以及自定義聚合函式,但是一定要注意agg方法只能進行聚合操作,進行其他例如:排序,這些方法是會報錯的。agg返回的是資料的標量,所以有些時候並不適合使用agg,這就要看我們接下來的操作了。
3.4.3、apply
GroupBy當中自由度最高的方法就是apply,它會將待處理的物件拆分為多個片段,然後各個片段分別呼叫傳入的函式,最後將它們組合到一起。
df.apply(
['func', 'axis=0', 'broadcast=None', 'raw=False', 'reduce=None', 'result_type=None', 'args=()', '**kwds']
func:傳入一個自定義函式
axis:函式傳入引數當axis=1就會把一行資料作為Series的資料
# 分析歐洲盃和歐洲冠軍聯賽決賽名單
import pandas as pd
url="https://en.wikipedia.org/wiki/List_of_European_Cup_and_UEFA_Champions_League_finals"
eu_champions=pd.read_html(url) # 獲取資料
a1 = eu_champions[2] # 取出決賽名單
a1.columns = a1.loc[0] # 使用第一行的資料替換預設的橫向索引
a1.drop(0,inplace=True) # 將第一行的資料刪除
a1.drop('#',axis=1,inplace=True) # 將以#為列名的那一列刪除
a1.columns=['Season', 'Nation', 'Winners', 'Score', 'Runners_up', 'Runners_up_Nation', 'Venue','Attendance'] # 設定列名
a1.tail() # 檢視後五行資料
a1.drop([64,65],inplace=True) # 刪除其中的缺失行以及無用行
a1
執行結果:
現在想根據分組選出Attendance
列中值最高的三個。
# 先自定義一個函式
def top(df,n=3,column='Attendance'):
return df.sort_values(by=column)[-n:]
top(a1,n=3)
執行結果:
接下來,就對a1分組並且使用apply呼叫該函式:
a1.groupby('Nation').apply(top)
執行之後會發現,我們通過這個操作將每個國家各個年份時段出席的人數的前三名進行了一個提取。
以上top函式是在DataFrame的各個片段上呼叫,然後結果又通過pandas.concat組裝到一起,並且以分組名稱進行了標記。
以上只是基本用法,apply的強大之處就在於傳入函式能做什麼都由自己說了算,它只是返回一個pandas物件或者標量值就行
3.5、其他常用方法
pandas常用方法(適用Series和DataFrame)
- mean(axis=0,skipna=False)
- sum(axis=1)
- sort_index(axis, …, ascending) # 按行或列索引排序
- sort_values(by, axis, ascending) # 按值排序
- apply(func, axis=0) # 將自定義函式應用在各行或者各列上,func可返回標量或者Series
- applymap(func) # 將函式應用在DataFrame各個元素上
- map(func) # 將函式應用在Series各個元素上