Pandas 使用指南(上) 基本數據結構
http://blkstone.github.io/2015/11/21/pandas-tutorial-1/
一、Pandas介紹
終于寫到了作者最想介紹,同時也是Python在數據處理方面功能最為強大的擴展模塊了。在處理實際的金融數據時,一個條數據通常包含了多種類型的數據,例如,股票的代碼是字符串,收盤價是浮點型,而成交量是整型等。在C++中可以實現為一個給定結構體作為單元的容器,如向量(vector,C++中的特定數據結構)。在Python中,pandas包含了高級的數據結構Series和DataFrame,使得在Python中處理數據變得非常方便、快速和簡單。
pandas不同的版本之間存在一些不兼容性,為此,我們需要清楚使用的是哪一個版本的pandas。現在我們就查看一下量化實驗室的pandas版本:
import pandas as pd
pd.__version__
'0.14.1'
pandas主要的兩個數據結構是Series和DataFrame,隨后兩節將介紹如何由其他類型的數據結構得到這兩種數據結構,或者自行創建這兩種數據結構,我們先導入它們以及相關模塊:
import numpy as np
import numpy as np
import pandas as pd
二、Pandas數據結構:Series
從一般意義上來講,Series可以簡單地被認為是** 帶索引的一維數組(或一維表) **。Series和一維數組最主要的區別在于Series類型具有索引(index),可以和另一個編程中常見的數據結構哈希(Hash)聯系起來。
2.1 創建Series
創建一個Series的基本格式是s = Series(data, index=index, name=name),以下給出幾個創建Series的例子。首先我們從數組創建Series:
a = np.random.randn(5)
print "a is an array:"
print a
s = Series(a)
print "s is a Series:"
print s
可以在創建Series時添加index,并可使用Series.index查看具體的index。需要注意的一點是,當從數組創建Series時,若指定index,那么index長度要和data的長度一致:
s = Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print ss.index
創建Series的另一個可選項是name,可指定Series的名稱,可用Series.name訪問。在隨后的DataFrame中,每一列的列名在該列被單獨取出來時就成了Series的名稱:
s = Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'], name='my_series')
print s
print s.name
Series還可以從字典(dict)創建:
d = {'a': 0., 'b': 1, 'c': 2}
print "d is a dict:"
print d
s = Series(d)
print "s is a Series:"
print s
讓我們來看看使用字典創建Series時指定index的情形(index長度不必和字典相同):
Series(d, index=['b', 'c', 'd', 'a'])
我們可以觀察到兩點:一是字典創建的Series,數據將按index的順序重新排列;二是index長度可以和字典長度不一致,如果多了的話,pandas將自動為多余的index分配NaN(not a number,pandas中數據缺失的標準記號),當然index少的話就截取部分的字典內容。
如果數據就是一個單一的變量,如數字4,那么Series將重復這個變量:
Series(4., index=['a', 'b', 'c', 'd', 'e'])
2.2 Series數據的訪問
訪問Series數據可以和數組一樣使用下標,也可以像字典一樣使用索引,還可以使用一些條件過濾:
s = Series(np.random.randn(10),index=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'])
s[0]
使用切片技巧
s[:2]
s[[2,0,4]]
s[['e', 'i']]
s[s > 0.5]
'e' in s => True
三、Pandas數據結構:DataFrame
在使用DataFrame之前,我們說明一下DataFrame的特性。DataFrame是將數個Series按列合并而成的二維數據結構,每一列單獨取出來是一個Series,這和SQL數據庫中取出的數據是很類似的。所以,按列對一個DataFrame進行處理更為方便,用戶在編程時注意培養按列構建數據的思維。DataFrame的優勢在于可以方便地處理不同類型的列,因此,就不要考慮如何對一個全是浮點數的DataFrame求逆之類的問題了,處理這種問題還是把數據存成NumPy的matrix類型比較便利一些。
3.1 創建DataFrame
首先來看如何從字典創建DataFrame。DataFrame是一個二維的數據結構,是多個Series的集合體。我們先創建一個值是Series的字典,并轉換為DataFrame:
d = {'one': Series([1., 2., 3.], index=['a', 'b', 'c']), 'two': Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
df = DataFrame(d)
print df
可以指定所需的行和列,若字典中不含有對應的元素,則置為NaN:
df = DataFrame(d, index=['r', 'd', 'a'], columns=['two', 'three'])
print df
可以使用dataframe.index和dataframe.columns來查看DataFrame的行和列,dataframe.values則以數組的形式返回DataFrame的元素:
print "DataFrame index:"
print df.index
print "DataFrame columns:"
print df.columns
print "DataFrame values:"
print df.values
DataFrame也可以從值是數組的字典創建,但是各個數組的長度需要相同:
d = {'one': [1., 2., 3., 4.], 'two': [4., 3., 2., 1.]}
df = DataFrame(d, index=['a', 'b', 'c', 'd'])print df
值非數組時,沒有這一限制,并且缺失值補成NaN:
d= [{'a': 1.6, 'b': 2}, {'a': 3, 'b': 6, 'c': 9}]
df = DataFrame(d)
print df
在實際處理數據時,有時需要創建一個空的DataFrame,可以這么做:
df = DataFrame()
print df
另一種創建DataFrame的方法十分有用,那就是使用concat函數基于Serie或者DataFrame創建一個DataFrame
a = Series(range(5))b = Series(np.linspace(4, 20, 5))df = pd.concat([a, b], axis=1)
print df
其中的axis=1表示按列進行合并,axis=0表示按行合并,并且,Series都處理成一列,所以這里如果選axis=0的話,將得到一個10×1的DataFrame。下面這個例子展示了如何按行合并DataFrame成一個大的DataFrame:
df = DataFrame()
index = ['alpha', 'beta', 'gamma', 'delta', 'eta']
for i in range(5):
a = DataFrame([np.linspace(i, 5*i, 5)], index=[index[i]])
print a df = pd.concat([df, a], axis=0)
print df
3.2 DataFrame數據的訪問
首先,再次強調一下DataFrame是以列作為操作的基礎的,全部操作都想象成先從DataFrame里取一列,再從這個Series取元素即可。可以用datafrae.column_name選取列,也可以使用dataframe[]操作選取列,我們可以馬上發現前一種方法只能選取一列,而后一種方法可以選擇多列。若DataFrame沒有列名,[]可以使用非負整數,也就是“下標”選取列;若有列名,則必須使用列名選取,另外datafrae.column_name在沒有列名的時候是無效的:
print df[1]
print type(df[1])
df.columns = ['a', 'b', 'c', 'd', 'e']
print df['b']
print type(df['b'])
print df.b
print type(df.b)
print df[['a', 'd']]
print type(df[['a', 'd']])
以上代碼使用了dataframe.columns為DataFrame賦列名,并且我們看到單獨取一列出來,其數據結構顯示的是Series,取兩列及兩列以上的結果仍然是DataFrame。訪問特定的元素可以如Series一樣使用下標或者是索引:
print df['b'][2]
print df['b']['gamma']
若需要選取行,可以使用dataframe.iloc按下標選取,或者使用dataframe.loc按索引選取:
print df.iloc[1]
print df.loc['beta']
選取行還可以使用切片的方式或者是布爾類型的向量:
print "Selecting by slices:"
print df[1:3]
bool_vec = [True, False, True, True, False]
print "Selecting by boolean vector:"
print df[bool_vec]
行列組合起來選取數據:
print df[['b', 'd']].iloc[[1, 3]]
print df.iloc[[1, 3]][['b', 'd']]
print df[['b', 'd']].loc[['beta', 'delta']]
print df.loc[['beta', 'delta']][['b', 'd']]
如果不是需要訪問特定行列,而只是某個特殊位置的元素的話,dataframe.at和dataframe.iat是最快的方式,它們分別用于使用索引和下標進行訪問:
print df.iat[2, 3]
print df.at['gamma', 'd']
dataframe.ix可以混合使用索引和下標進行訪問,唯一需要注意的地方是行列內部需要一致,不可以同時使用索引和標簽訪問行或者列,不然的話,將會得到意外的結果:
print df.ix['gamma', 4]
print df.ix[['delta', 'gamma'], [1, 4]]
print df.ix[[1, 2], ['b', 'e']]
print "Unwanted result:"
print df.ix[['beta', 2], ['b', 'e']]
print df.ix[[1, 2], ['b', 4]]
參考資料
[1] 量化分析師的Python日記【第5天:數據處理的瑞士軍刀pandas】
[2] pandas文檔