學機器學習,不會數據處理怎么行?—— 二、Pandas詳解


在上篇文章學機器學習,不會數據處理怎么行?—— 一、NumPy詳解中,介紹了NumPy的一些基本內容,以及使用方法,在這篇文章中,將接着介紹另一模塊——Pandas。(本文所用代碼在這里

Pandas數據結構介紹

大家應該都聽過表結構,但是,如果讓你自己來實現這么一個結構,並且能對其進行數據處理,能實現嗎?我相信,大部分人都能做出來,但是不一定能做的很好。而Python中的一個模塊pandas給我們提供了一個很好的數據結構,它包括了序列Series和數據框DataFrame。pandas是基於NumPy數組構建的,特別是基於數組的函數和不使用for循環的數據處理,讓以Numpy為中心的應用變得更加簡單。

Series

創建方式

Series是一種類似於一維數組的對象,它由一組數據(各種NumPy數據類型)以及一組與之相關的數據標簽(即索引)組成,其創建主要有三種方式

1)通過一維數組創建序列

import numpy as np
import pandas as pd
arr1 = np.arange(10)
s1 = pd.Series(arr1)

通過type函數可查看arr1與s1的類型分別為,numpy.ndarray 和 pandas.core.series.Series。

3)通過字典的方式創建

dic1 = {'a': 1,'b': 2,'c': 3,'d': 4,'e': 50}
s2 = pd.Series(dic1)

通過type函數可查看dic1與s2的類型分別為,dict 和 pandas.core.series.Series。

3)通過DataFrame中的某一行或者某一列創建序列

這一步將在下面講DataFrame的時候再補充

Series索引

我們上面在創建Series序列時,可以輸出查看我們創建的Series長什么樣,發現有兩列,第二列的數據跟我們輸入的是一樣的,那第一列是什么呢?那就是索引。Series的字符串表現形式為:索引在左邊,值在右邊。如果我們沒有為數據指定索引,就會自動創建一個0到N-1(N為數據的長度)的整數型索引。

 我們可以通過 Series 的 values 和 index 屬性獲取其數組表示形式和索引對象。

obj = pd.Series([6, 9, -8, 1])
obj.values
obj.index

我們也能修改其索引

obj.index = ['a','b','c','d']

講了那么多,知道這是索引了,但它有什么用呢? 

1.我們可以通過索引值或索引標簽進行數據的獲取

obj['a']
obj[3]
obj[[0,2,3]]
obj[0:2]
obj['a':'c']

注:若通過索引標簽獲取數據的話,末端標簽對應的值也將返回!!!

2.自動化對齊

如果有兩個序列,需要對這兩個序列進行算術運算,這時索引的存在就體現的它的價值了—自動化對齊.

obj1 = pd.Series(np.array([10,15,20,30,55,80]),index = ['a','b','c','d','e','f'])
obj2 = pd.Series(np.array([12,11,13,15,14,16]),index = ['a','c','g','b','d','f'])
obj1+obj2
obj1*obj2

我們會發現,計算后的序列號中,g和e對應的值為NaN,這是因為obj1中沒有g索引,obj2中沒有e索引,所以數據的運算會產生兩個缺失值NaN。

DataFrame

DataFrame是一個表格型的數據結構,它含有一組有序的列,每列可以是不同的值類型(數值、字符串、布爾值等)。DataFrame既有行索引也有列索引,它可以被看做由Series組成的字典。

創建方式

1)通過二維數組創建

arr2 = np.array(np.arange(12).reshape(4,3))
df1 = pd.DataFrame(arr2)

 

2)通過字典的方式創建

該方式可通過字典列表和嵌套字典實現,如果將嵌套字典傳給DataFrame,pandas就會被解釋為:外層字典的鍵作為列,內層鍵則作為行索引

#字典列表
dic2 = {'a': [1, 2, 3, 4],'b': [5, 6, 7, 8],'c': [9, 10, 11, 12],'d': [13, 14, 15, 16]}
df2 = pd.DataFrame(dic2)

#嵌套字典
dic3 = {'one': {'a': 1,'b': 2,'c': 3,'d':4},
        'two': {'a': 5,'b': 6,'c': 7,'d': 8},
        'three':{'a': 9,'b': 10,'c': 11,'d':12}}
df3 = pd.DataFrame(dic3)

 

3)通過數據框的方式創建

df4 = df3[['one','three']]

s3 = df4['one']

 

DataFrame索引

DataFrame的索引與Series的索引大體上是一樣的,不過DataFrame有兩個索引,分別是列索引和行索引,感覺看起來就跟excel差不多了。具體的實現方式可通過下面的部分來了解。

利用pandas進行數據處理

pandas可以通過布爾索引有針對的選取原數據的子集、指定行、指定列等,同時我們可以通過 pd.read_csv()來導入數據集,因為暫時找不到數據集,就從別人的代碼里復制一些過來了

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002, 2003],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)

 

選擇數據

簡單篩選

我們可以使用以下函數來查詢數據的前幾行或后幾行(默認5行)

frame.head()
frame.tail()

也可以使用行列索引來簡單地查詢指定的行和列

frame['state']
frame[0:3]

 

loc

當然,上面的方式只是比較簡單的操作,復雜些的,我們可以通過 loc 來選取數據(: 表示所有行)

frame.loc[:,['state','year']]

 

iloc

另外,我們也可以使用iloc,通過位置選擇在不同情況下所需要的數據,可進行選取某個數據、連續或者跨行選擇等操作

frame.iloc[0:2,0:2]

 

ix

除此之外還有一種操作方式,使用 混合選擇 ix ,我這里的表可能無法很好的體現,如果將行索引改為字符就可以看出來了

frame.ix[3:5,['year']]

發現輸入這行代碼運行之后發出一條警告

DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

emmmmm。。想想以后主要還是用 loc 和 iloc 好了。

通過判斷的篩選

除了上面講的之外,我們還可以通過判斷指令進行選擇,即通過約束某項條件然后選擇出當前所有數據。

frame[frame['year']>2001]

 

數據修改

講完了數據選擇,既然選擇出了數據,我們可能會想去修改數據,那么,怎么修改呢?

根據位置

我們可以利用索引或者標簽確定需要修改的位置

frame.iloc[0,2]=1.6
frame.loc[1,'pop']=2

 

根據條件

如果我們想將數據中'pop'欄小於1.7的'year'改為1999,那么我們可以進行如下操作

frame.year[frame['pop']<1.7]=1999

 

按行或按列設置

我們還可以對行列進行批處理

frame['pop']=2.6

 

添加和刪除數據

上面講的都是數據的修改,再來講講數據的添加和刪除

#數據添加
#方法一
frame['A'] = np.nan
#方法二
frame['B'] = pd.Series([1, 2, 3, 4, 5, 6])

#數據刪除 
frame.drop('B',axis = 1)

注:

  • 對於方法二,長度必須對齊,如果行索不為默認則需指定索引
dates = pd.date_range('20130101', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates, columns=['A','B','C','D'])
df['E'] = pd.Series([1, 2, 3, 4, 5, 6],index=pd.date_range('20130101',periods=6))

 

  • 對於數據刪除,axis取值 0表示刪除行索引 1表示刪除列索引 默認為0

統計分析

pandas模塊為我們提供了非常多的描述性統計分析的指標函數,如總和、均值、最小值、最大值等,跟對應的NumPy數組方法相比,它們都是基於沒有缺失數據的假設而構建的,下圖中列舉了大部分函數

這里有幾點需要注意

  • descirbe函數只能針對序列或者數據框,一維數組是沒有這個方法的。
  • 使用sum時,NaN值會自動被排除,除非整個切片都是NaN,通過skipna選項可以禁用該功能。
  • 調用sum時,返回的時含有列的和的Series,需要傳入axis的值才會進行行運算

當我們要對一列數據進行分析時,我們可以將要分析的內容封裝成一個函數,方便調用

def stats(x):
    return pd.Series([x.count(),x.min(),x.idxmin(),
    x.quantile(.25),x.median(),
    x.quantile(.75),x.mean(),
    x.max(),x.idxmax(),
    x.mad(),x.var(),
    x.std(),x.skew(),x.kurt()],
    index = ['Count','Min','Whicn_Min',
    'Q1','Median','Q3','Mean',
    'Max','Which_Max','Mad',
    'Var','Std','Skew','Kurt'])

有時或許會需要對一個數據框進行分析,那該如何操作?是一列一列不斷地取出來然后調用函數嗎?這個方法也不是不可行,但面對眾多數據時該怎么辦?一個一個調用怕不是要弄到明年,在這里我們可以使用apply函數

d1 = pd.Series(2*np.random.normal(size = 100)+3)
d2 = np.random.f(2,4,size = 100)
d3 = np.random.randint(1,100,size = 100)
df = pd.DataFrame(np.array([d1,d2,d3]).T,columns=['x1','x2','x3'])
df.head()
df.apply(stats)

就這樣很簡單的創建了數值型數據的統計性描述。但如果是離散型數據呢?就不能用這個統計口徑了,我們需要統計離散變量的觀測數、唯一值個數、眾數水平及個數。你只需要使用describe方法就可以實現這樣的統計了。

缺失值處理

現實生活中的數據是非常雜亂的,其中缺失值也是非常常見的,對於缺失值的存在可能會影響到后期的數據分析或挖掘工作,那么我們該如何處理這些缺失值呢?常用的有三大類方法,即刪除法、填補法和插值法。

刪除法:當數據中的某個變量大部分值都是缺失值,可以考慮刪除改變量;當缺失值是隨機分布的,且缺失的數量並不是很多是,也可以刪除這些缺失的觀測。
替補法:對於連續型變量,如果變量的分布近似或就是正態分布的話,可以用均值替代那些缺失值;如果變量是有偏的,可以使用中位數來代替那些缺失值;對於離散型變量,我們一般用眾數去替換那些存在缺失的觀測。
插補法:插補法是基於蒙特卡洛模擬法,結合線性模型、廣義線性模型、決策樹等方法計算出來的預測值替換缺失值。

刪除法

這里僅講述刪除法和替補法,先來看刪除法,先建立一個6X4的矩陣數據並且把兩個位置置為空

dates = pd.date_range('20130101', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates, columns=['A','B','C','D'])
df.iloc[0,1] = np.nan
df.iloc[1,2] = np.nan

我們可以結合sum函數和isnull函數來檢測數據中含有多少缺失值

for i in df.columns:
    print(sum(pd.isnull(df[i])))

也可以通過any來判斷是否存在缺失值

np.any(df.isnull()) == True  

刪除直接dropna就行了

df.dropna(
    axis=0,     # 0: 對行進行操作; 1: 對列進行操作 默認為0
    how='any'   # 'any': 只要存在 NaN 就 drop 掉; 'all': 必須全部是 NaN 才 drop 默認為'any'
    ) 

填補法

簡單粗暴的辦法就是把所有的NaN用0代替

df.fillna(value=0)

稍微好一點的方法,使用常量填充不同的列

df.fillna({'B': 3,'C': 4})

還算好的方法,采用前項填充或后向填充

df.fillna(method = 'ffill') #用前一個觀測值填充
df.fillna(method = 'bfill') #用后一個觀測值填充

較好的方法,用均值或中位數填充各自的列

df.fillna(df.median())
df.fillna(df.mean())

很顯然,在使用填充法時,相對於常數填充或前項、后項填充,使用各列的眾數、均值或中位數填充要更加合理一點,這也是工作中常用的一個快捷手段。

注:使用fillna,dropna時,需要添加參數 inplace = True,如df.fillna(df.median(),inplace = True),以確認修改,否則實際的數據並不會有改動。

數據合並

concat

pandas 處理多組數據的時候往往會要用到數據的合並處理,使用 concat 是一種基本的合並方式.而且concat中有很多參數可以調整,合並成你想要的數據形式.

先創建數據集

df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','d'])
df3 = pd.DataFrame(np.ones((3,4))*2, columns=['a','b','c','d'])

concat合並

pd.concat([df1, df2, df3], axis=0) #縱向合並
pd.concat([df1, df2, df3], axis=1) #橫向合並

這里觀察下縱向合並的輸出,會發現index的值為0,1,2,0,1,2,0,1,2,大部分情況下,這肯定不是我們想要的,我們可以通過一下方法來重置index

pd.concat([df1, df2, df3], axis=0, ignore_index=True)

pandas中的數據結構是支持類似SQL的部分操作方式的,如添加新行新列、多表連接、聚合什么的。pandas有個join參數,用來定義連接方式的,默認為outer,此方式是依照column來做縱向合並有相同的column上下合並在一起,其他的獨自成列,沒有的值用NaN填充

df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'], index=[1,2,3])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['b','c','d','e'], index=[2,3,4])
res = pd.concat([df1, df2], axis=0, join='outer')#縱向"外"合並df1與df2

參數:

  • axis 表示行或列,0為行,1為列
  • join 'inner':內連接,會產生NaN的列丟棄 ‘outer’:外連接,上面介紹過了

另外,還有join_axes也可以決定連接方式

pd.concat([df1, df2], axis=1, join_axes=[df1.index])
pd.concat([df1, df2], axis=1, join_axes=[df2.index])

運行上面兩行代碼,就會發現有所不同,第一行是以df1的index為依據,將df2中具有相同索引的行對應拼接上去,第二行同樣的道理。

此外,還有append操作,該操作類似與axis=0,join='outer'時的操作,不過要注意的是append只有縱向合並。

merge

pandas中的合並操作除了concat之外,還有merge操作,主要用於兩組有key column的數據,統一索引的數據. 通常也被用在Database的處理當中.

看個簡單的,依據一組Key合並

left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
                             'A': ['A0', 'A1', 'A2', 'A3'],
                             'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
                              'C': ['C0', 'C1', 'C2', 'C3'],
                              'D': ['D0', 'D1', 'D2', 'D3']})
pd.merge(left, right, on='key')

稍微難一點就是依據兩組Key合並,合並時有四種方法 how=['left','right','outer','inner'],默認為how='inner'

left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
                      'key2': ['K0', 'K1', 'K0', 'K1'],
                      'A': ['A0', 'A1', 'A2', 'A3'],
                      'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
                       'key2': ['K0', 'K0', 'K0', 'K0'],
                       'C': ['C0', 'C1', 'C2', 'C3'],
                       'D': ['D0', 'D1', 'D2', 'D3']})

res = pd.merge(left, right, on=['key1', 'key2'], how='inner')
print(res)

res = pd.merge(left, right, on=['key1', 'key2'], how='outer')
print(res)

res = pd.merge(left, right, on=['key1', 'key2'], how='left')
print(res)

res = pd.merge(left, right, on=['key1', 'key2'], how='right')
print(res)

都試一試然后輸出就知道是如何操作的了。

 上面講的是以列索引為標准進行合並,當然,我們還可以以index為標准進行合並

left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
                     'B': ['B0', 'B1', 'B2']},
                     index=['K0', 'K1', 'K2'])
right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
                      'D': ['D0', 'D2', 'D3']},
                     index=['K0', 'K2', 'K3'])

res = pd.merge(left, right, left_index=True, right_index=True, how='outer')
print(res)

res = pd.merge(left, right, left_index=True, right_index=True, how='inner')
print(res)

 聚合和分組

pandas提供了一個靈活高效的groupby功能,它使你能以一種自然的方式對數據集進行切片、切塊、摘要等操作。根據一個或多個鍵(可以是函數、數組或DataFrame列名)拆分pandas對象。計算分組摘要統計,如計數、平均值、標准差,或用戶自定義函數。

根據列索引分組

df = pd.DataFrame({'key1':['a', 'a', 'b', 'b', 'a'],
        'key2':['one', 'two', 'one', 'two', 'one'],
        'data1':np.random.randn(5),
        'data2':np.random.randn(5)})
df['data1'].groupby(df['key1']).mean()

這里我們按照key1將數據分組,然后計算分組后的data1的平均值。

我們也可以一次傳入多個數組

df['data1'].groupby([df['key1'], df['key2']]).mean()

此外,還可以將列名用作分組

df.groupby('key1').mean()
df.groupby(['key1', 'key2']).mean()

注:這里在執行df.groupby('key1').mean()時,結果中沒有key2列。這是因為df['key2']不是數值數據,所以被從結果中排除了。默認情況下,所有數值列都會被聚合,雖然有時可能會被過濾為一個子集。

分組后可進行迭代

for name, group in df.groupby('key1'):
    print(name)
    print(group)

對於多重鍵的情況

for (k1, k2), group in df.groupby(['key1', 'key2']):
     print (k1, k2)
     print (group)

 上面進行的group操作返回的類型都是Series,如果我們想返回DataFrame類型的結果,那么我們可以進行如下操作

df[['data2']].groupby([df['key1']])

 

根據行類型進行分組

 groupby默認是在axis=0上進行分組的,通過設置也可以在其他任何軸上進行分組。拿上面例子中的df來說,我們可以根據行類型(dtype)對列進行分組

dict(list(df.groupby(df.dtypes,axis = 1)))

這方面還有蠻多內容,不過都還沒接觸過,只能暫時講到這里了,如果要了解更多,可以訪問這篇博客 ,我在后面也會逐漸完善這些方面的內容。

結尾

講了這么多,相信你對pandas肯定有了一定的了解了,但由於個人接觸不多,就暫時介紹到這里,pandas還有很多內容如數據透視表、多層索引的使用、數據清洗等,將在后面學到時再做補充


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM