《利用Python進行數據分析》五章·Pandas入門·學習筆記(四·完)
一、pandas簡介
pandas含有使數據清洗和分析工作變得更快更簡單的數據結構和操作工具。pandas經常和其它工具一同使用,如數值計算工具NumPy和SciPy,分析庫statsmodels和scikit-learn,和數據可視化庫matplotlib。pandas是基於NumPy數組構建的,特別是基於數組的函數和不使用for循環的數據處理。
pandas是專門為處理表格和混雜數據設計的。而NumPy更適合處理統一的數值數組數據。pandas引入約定:
In [1]: import pandas as pd
因此,只要你在代碼中看到pd.,就得想到這是pandas。因為Series和DataFrame用的次數非常多,所以將其引入本地命名空間中會更方便:
In [2]: from pandas import Series, DataFrame
二、實例
2.12排序和排名
根據條件對數據集排序也是一種重要的內置運算。要對行或列索引按字典順序進行排序,可使用sort_index方法,它將返回一個已排序的新對象:
In [201]: obj = pd.Series(range(4), index=[d, a, b, c])
In [202]: obj.sort_index()
Out[202]:
a 1
b 2
c 3
d 0
dtype: int64
對於DataFrame,則可以根據任意一個軸上的索引進行排序:
In [203]: frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
.....: index=[three, one],
.....: columns=[d, a, b, c])
In [204]: frame.sort_index()
Out[204]:
d a b c
one 4 5 6 7
three 0 1 2 3
In [205]: frame.sort_index(axis=1)
Out[205]:
a b c d
three 1 2 3 0
one 5 6 7 4
數據默認是按升序排序的,但也可以降序排序:
In [206]: frame.sort_index(axis=1, ascending=False)
Out[206]:
d c b a
three 0 3 2 1
one 4 7 6 5
若要按值對Series進行排序,可使用其sort_values方法:
In [207]: obj = pd.Series([4, 7, -3, 2])
In [208]: obj.sort_values() #按數值排列
Out[208]:
2 -3
3 2
0 4
1 7
dtype: int64
在排序時,任何缺失值默認都會被放到Series的末尾:
In [209]: obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
In [210]: obj.sort_values()
Out[210]:
4 -3.0
5 2.0
0 4.0
2 7.0
1 NaN
3 NaN
dtype: float64
當排序一個DataFrame時,你可能希望根據一個或多個列中的值進行排序。將一個或多個列的名字傳遞給sort_values的by選項即可達到該目的:
In [211]: frame = pd.DataFrame({b: [4, 7, -3, 2], a: [0, 1, 0, 1]})
In [212]: frame #行號自動生成
Out[212]:
a b
0 0 4
1 1 7
2 0 -3
3 1 2
In [213]: frame.sort_values(by=b) #依據b列從小到大排序
Out[213]:
a b
2 0 -3
3 1 2
0 0 4
1 1 7
要根據多個列進行排序,傳入名稱的列表即可:
In [214]: frame.sort_values(by=[a, b])#優先按a列,如a相同則按b
Out[214]:
a b
2 0 -3
0 0 4
3 1 2
1 1 7
接下來介紹Series和DataFrame的rank方法。默認情況下,rank通過「為各組分配一個平均排名」的方式體現排名:
In [215]: obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
In [216]: obj.rank()
Out[216]:
0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64
註:7在整個Series中出現兩次,按從小到大排列分別排在第六和第七,所以取兩個排名的均值,即6.5,其餘類似.
也可以根據值在原數據中出現的順序給出排名:
In [217]: obj.rank(method=first)
Out[217]:
0 6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
dtype: float64
這裡,條目0和2沒有使用平均排名6.5,它們被設成了6和7,因為數據中標籤0位於標籤2的前面。
你也可以按降序進行排名:
In [218]: obj.rank(ascending=False, method=max)
Out[218]:
0 2.0
1 7.0
2 2.0
3 4.0
4 5.0
5 6.0
6 4.0
dtype: float64
此處的max表示當出現兩個一樣的排名,如7出現兩次,按從小到大排列分別排在第六和第七,則取排名較大的,取7,ascending默認為True,從小到大,改為false則倒序排列。
DataFrame可以在行或列上計算排名:
In [219]: frame = pd.DataFrame({b: [4.3, 7, -3, 2], a: [0, 1, 0, 1], c: [-2, 5, 8, -2.5]})
In [220]: frame
Out[220]:
b a c
0 4.3 0 -2.0
1 7.0 1 5.0
2 -3.0 0 8.0
3 2.0 1 -2.5
In [221]: frame.rank(axis=columns) #每個元素在同行不同列中的排名
Out[221]:
b a c
0 3.0 2.0 1.0
1 3.0 1.0 2.0
2 1.0 2.0 3.0
3 3.0 2.0 1.0
2.13 帶有重複標籤的軸索引
直到目前為止,我所介紹的所有範例都有著唯一的軸標籤(索引值)。雖然許多pandas函數都要求標籤唯一,但這並不是強制性的。我們來看看下面這個簡單的帶有重複索引值的Series:
In [222]: obj = pd.Series(range(5), index=[a, a, b, b, c])
In [223]: obj
Out[223]:
a 0
a 1
b 2
b 3
c 4
dtype: int64
索引的is_unique屬性可以告訴你它的值是否唯一:
In [224]: obj.index.is_unique
Out[224]: False
對於帶有重複值的索引,數據選取的行為將會有些不同。如果某個索引對應多個值,則返回一個Series;而對應單個值的,則返回一個標量值:
In [225]: obj[a]#索引重複,則返回Series
Out[225]:
a 0
a 1
dtype: int64
In [226]: obj[c]
Out[226]: 4
這樣會使代碼變複雜,因為索引的輸出類型會根據標籤是否有重複發生變化。
對DataFrame的行進行索引時也是如此:
In [227]: df = pd.DataFrame(np.random.randn(4, 3), index=[a, a, b, b])
In [228]: df
Out[228]:
0 1 2
a 0.274992 0.228913 1.352917
a 0.886429 -2.001637 -0.371843
b 1.669025 -0.438570 -0.539741
b 0.476985 3.248944 -1.021228
In [229]: df.loc[b]#返回所有索引為b的字元
Out[229]:
0 1 2
b 1.669025 -0.438570 -0.539741
b 0.476985 3.248944 -1.021228
2.14 匯總和計算描述統計
pandas對象擁有一組常用的數學和統計方法。它們大部分都屬於約簡和匯總統計,用於從Series中提取單個值或從DataFrame的行或列中提取一個Series。跟對應的NumPy數組方法相比,它們都是基於沒有缺失數據的假設而構建的。看一個簡單的DataFrame:
In [230]: df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],
.....: [np.nan, np.nan], [0.75, -1.3]],
.....: index=[a, b, c, d],
.....: columns=[one, two])
In [231]: df
Out[231]:
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
調用DataFrame的sum方法將會返回一個含有列的和的Series:
In [232]: df.sum() #按列求和,nan計為0
Out[232]:
one 9.25
two -5.80
dtype: float64
傳入axis=columns或axis=1將會按行進行求和運算:
In [233]: df.sum(axis=1)#按行求各列的和,nan計為0
Out[233]:
a 1.40
b 2.60
c NaN
d -0.55
NA值會自動被排除,除非整個切片都是NA。通過skipna選項可以禁用該功能:
In [234]: df.mean(axis=columns, skipna=False)
Out[234]:
a NaN
b 1.300
c NaN
d -0.275
dtype: float64
有些方法返回的是間接統計(比如達到最小值或最大值的索引):
In [235]: df.idxmax() #每列最大值的行號
Out[235]:
one b
two d
dtype: object
另一些方法則是累計型的:
In [236]: df.cumsum() #每列逐行累加求和
Out[236]:
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
還有一種方法,它既不是約簡型也不是累計型。describe就是一個例子,它用於一次性產生多個匯總統計:
In [237]: df.describe()
Out[237]:
one two
count 3.000000 2.000000 #可用元素
mean 3.083333 -2.900000 #平均數
std 3.493685 2.262742 #標準差
min 0.750000 -4.500000 #最小值
25% 1.075000 -3.700000 #分位數
50% 1.400000 -2.900000 #分位數
75% 4.250000 -2.100000 #分位數
max 7.100000 -1.300000 #最大值
對於非數值型數據,describe會產生另外一種匯總統計:
In [238]: obj = pd.Series([a, a, b, c] * 4)
In [239]: obj.describe()#obj相當於把列表重複了4次的一個Series
Out[239]:
count 16
unique 3 #不重複個數
top a #出現最多的元素
freq 8 #top元素出現次數
dtype: object
2.15相關係數與協方差
有些匯總統計是通過參數對計算出來的。我們來看幾個DataFrame,它們的數據來自股票價格和成交量,使用的是pandas-datareader包,該包可以便捷地獲取一些數據:
conda install pandas-datareader
我使用pandas_datareader模塊下載了一些股票數據:
import pandas_datareader.data as web
all_data = {ticker: web.get_data_yahoo(ticker)
for ticker in [AAPL, IBM, MSFT, GOOG]}
price = pd.DataFrame({ticker: data[Adj Close]
for ticker, data in all_data.items()})
volume = pd.DataFrame({ticker: data[Volume]
for ticker, data in all_data.items()})
In [242]: returns = price.pct_change()
In [243]: returns.tail()
Out[243]:
AAPL IBM MSFT GOOG
Date
2019-04-29 0.001517 -0.002797 -0.000924 0.012105
2019-04-30 -0.019256 0.008774 0.006396 -0.076966
2019-05-01 0.049086 0.002067 -0.020827 -0.017165
2019-05-02 -0.006508 -0.006901 -0.013059 -0.004683
2019-05-03 0.012431 0.004728 0.021314 0.019602
Series的corr方法用於計算兩個Series中重疊的、非NA的、按索引對齊的值的相關係數。與此類似,cov用於計算協方差:
In [244]: returns[MSFT].corr(returns[IBM])#相關係數
Out[244]: 0.4852886668057154
In [245]: returns[MSFT].cov(returns[IBM])#協方差
Out[245]: 8.643793758110261e-05
因為MSTF是一個合理的Python屬性,我們還可以用更簡潔的語法選擇列:
In [246]: returns.MSFT.corr(returns.IBM)
Out[246]: 0.4852886668057154
另一方面,DataFrame的corr和cov方法將以DataFrame的形式分別返回完整的相關係數或協方差矩陣:
In [247]: returns.corr()
Out[247]:
AAPL IBM MSFT GOOG
AAPL 1.000000 0.369996 0.448064 0.455816
IBM 0.369996 1.000000 0.485289 0.403171
MSFT 0.448064 0.485289 1.000000 0.534275
GOOG 0.455816 0.403171 0.534275 1.000000
In [248]: returns.cov()
Out[248]:
AAPL IBM MSFT GOOG
AAPL 0.000269 0.000075 0.000106 0.000115
IBM 0.000075 0.000152 0.000086 0.000077
MSFT 0.000106 0.000086 0.000209 0.000119
GOOG 0.000115 0.000077 0.000119 0.000238
利用DataFrame的corrwith方法,你可以計算其列或行跟另一個Series或DataFrame之間的相關係數。傳入一個Series將會返回一個相關係數值Series(針對各列進行計算):
In [249]: returns.corrwith(returns.IBM)
Out[249]:
AAPL 0.386817
GOOG 0.405099
IBM 1.000000
MSFT 0.499764
dtype: float64
傳入一個DataFrame則會計算按列名配對的相關係數。這裡,我計算百分比變化與成交量的相關係數:
In [250]: returns.corrwith(volume)
Out[250]:
AAPL -0.075565
GOOG -0.007067
IBM -0.204849
MSFT -0.092950
dtype: float64
傳入axis=columns即可按行進行計算。無論如何,在計算相關係數之前,所有的數據項都會按標籤對齊。
2.16唯一值、值計數以及成員資格
還有一類方法可以從一維Series的值中抽取信息。看下面的例子:
In [251]: obj = pd.Series([c, a, d, a, a, b, b, c, c])
第一個函數是unique,它可以得到Series中的唯一值數組:
In [252]: uniques = obj.unique()
In [253]: uniques
Out[253]: array([c, a, d, b], dtype=object)
返回的唯一值是未排序的,如果需要的話,可以用uniques.sort()對結果再次進行排序。類似的情況,value_counts用於計算一個Series中各值出現的頻率:
In [254]: obj.value_counts()
Out[254]:
c 3
a 3
b 2
d 1
dtype: int64
為了便於查看,結果Series是按值頻率降序排列的。value_counts還是一個頂級pandas方法,可用於任何數組或序列:
In [255]: pd.value_counts(obj.values, sort=False)
Out[255]:
a 3
b 2
c 3
d 1
dtype: int64
isin用於判斷矢量化集合的成員資格,可用於過濾Series中或DataFrame列中數據的子集:
In [256]: obj
Out[256]:
0 c
1 a
2 d
3 a
4 a
5 b
6 b
7 c
8 c
dtype: object
In [257]: mask = obj.isin([b, c])
In [258]: mask
Out[258]:
0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
In [259]: obj[mask]#只取滿足條件的
Out[259]:
0 c
5 b
6 b
7 c
8 c
dtype: object
與isin類似的是Index.get_indexer方法,它可以給你一個索引數組,從可能包含重複值的數組到另一個不同值的數組:
In [260]: to_match = pd.Series([c, a, b, b, c, a])
In [261]: unique_vals = pd.Series([c, b, a])
In [262]: pd.Index(unique_vals).get_indexer(to_match)
#to_match中各元素在索引數組中的位置
Out[262]: array([0, 2, 1, 1, 0, 2])
有時,你可能希望得到DataFrame中多個相關列的一張柱狀圖。例如:
In [263]: data = pd.DataFrame({Qu1: [1, 3, 4, 3, 4],
.....: Qu2: [2, 3, 1, 2, 3],
.....: Qu3: [1, 5, 2, 4, 4]})
In [264]: data
Out[264]:
Qu1 Qu2 Qu3
0 1 2 1
1 3 3 5
2 4 1 2
3 3 2 4
4 4 3 4
將pandas.value_counts傳給該DataFrame的apply函數,就會出現:
In [265]: result = data.apply(pd.value_counts).fillna(0)
In [266]: result
Out[266]:
Qu1 Qu2 Qu3
1 1.0 1.0 1.0
2 0.0 2.0 1.0
3 2.0 2.0 0.0
4 2.0 0.0 2.0
5 0.0 0.0 1.0
這裡,結果中的行標籤是所有列的唯一值。後面的頻率值是每個列中這些值的相應計數。
三、小結
obj.sort_index()#Series索引排序
frame.sort_index()#DataFrame索引排序
frame.sort_index(axis=1)#DataFrame列排序
frame.sort_index(axis=1, ascending=False)#DataFrame列降序排序
obj.sort_values() #按數值排列
frame.sort_values(by=b) #依據b列從小到大排序
frame.sort_values(by=[a, b])#優先按a列,如a相同則按b
obj.rank() #Series的排名
obj.rank(method=first) #先出現的排名靠前
obj.rank(ascending=False, method=max) #降序,同個數值取排名大的
frame.rank(axis=columns) #每個元素在同行不同列中的排名
obj.index.is_unique#值是否唯一
obj[a]#索引重複,則返回Series
df.loc[b]#返回所有索引為b的字元
df.sum() #按列求和,nan計為0
df.sum(axis=1)#按行求各列的和,nan計為0
df.mean(axis=columns, skipna=False)#遇到NaN不跳過求平均數
df.idxmax() #每列最大值的行號
df.cumsum() #每列逐行累加求和
df.describe()
!import pandas_datareader.data as web #2.15 pandas_datareader的關聯功能相對重要,後續有可能專門寫一篇專項
uniques = obj.unique()#得到Series中的唯一值數組
obj.value_counts()#計算一個Series中各值出現的頻率
mask = obj.isin([b, c]) #是否包含列表元素
pd.Index(unique_vals).get_indexer(to_match)#to_match中各元素在索引數組中的位置
result = data.apply(pd.value_counts).fillna(0)
·後記
本章結束,我們接觸了通過pandas自帶的數據獲取工具去獲取一些數據並用DataFrame進行處理,這個是量化交易的良好開端。
目前篇幅有些過長,後續還是會合理壓縮一下,有什麼建議或 可以評論留言。
歡迎點贊·評論·分享·收藏·澆築樹苗
(o???o)? r (苗·lv.0)
推薦閱讀: