▲本文二級目錄
01 關于pandas
pandas,Python+data+analysis的組合縮寫,是Python中基于Numpy和Matplotlib的第三方數據分析庫,與后兩者共同構成了Python數據分析的基礎工具包,享有數分三劍客之名。
正因為pandas是在Numpy基礎上實現,其核心數據結構與Numpy的ndarray十分相似,但pandas與Numpy的關系不是替代,而是互為補充。二者之間主要區別是:
從數據結構上看:Numpy的核心數據結構是ndarray,支持任意維數的數組,但要求單個數組內所有數據是同質的,即類型必須相同;而pandas的核心數據結構是series和dataframe,僅支持一維和二維數據,但數據內部可以是異構數據,僅要求同列數據類型一致即可Numpy的數據結構僅支持數字索引,而pandas數據結構則同時支持數字索引和標簽索引從功能定位上看:Numpy雖然也支持字符串等其他數據類型,但仍然主要是用于數值計算,尤其是內部集成了大量矩陣計算模塊,例如基本的矩陣運算、線性代數、fft、生成隨機數等,支持靈活的廣播機制pandas主要用于數據處理與分析,支持包括數據讀寫、數值計算、數據處理、數據分析和數據可視化全套流程操作pandas主要面向數據處理與分析,主要具有以下功能特色:
按索引匹配的廣播機制,這里的廣播機制與Numpy廣播機制還有很大不同便捷的數據讀寫操作,相比于Numpy僅支持數字索引,pandas的兩種數據結構均支持標簽索引,包括bool索引也是支持的類比SQL的join和groupby功能,pandas可以很容易實現SQL這兩個核心功能,實際上,SQL的絕大部分DQL和DML操作在pandas中都可以實現類比Excel的數據透視表功能,Excel中最為強大的數據分析工具之一是數據透視表,這在pandas中也可輕松實現自帶正則表達式的字符串向量化操作,對pandas中的一列字符串進行通函數操作,而且自帶正則表達式的大部分接口豐富的時間序列向量化處理接口常用的數據分析與統計功能,包括基本統計量、分組統計分析等集成Matplotlib的常用可視化接口,無論是series還是dataframe,均支持面向對象的繪圖接口正是由于具有這些強大的數據分析與處理能力,pandas還有數據處理中"瑞士軍刀"的美名。
02 數據結構
pandas核心數據結構有兩種,即一維的series和二維的dataframe,二者可以分別看做是在Numpy一維數組和二維數組的基礎上增加了相應的標簽信息。正因如此,可以從兩個角度理解series和dataframe:
series和dataframe分別是一維和二維數組,因為是數組,所以Numpy中關于數組的用法基本可以直接應用到這兩個數據結構,包括數據創建、切片訪問、通函數、廣播機制等series是帶標簽的一維數組,所以還可以看做是類字典結構:標簽是key,取值是value;而dataframe則可以看做是嵌套字典結構,其中列名是key,每一列的series是value。所以從這個角度講,pandas數據創建的一種靈活方式就是通過字典或者嵌套字典,同時也自然衍生出了適用于series和dataframe的類似字典訪問的接口,即通過loc索引訪問。注意,這里強調series和dataframe是一個類字典結構而非真正意義上的字典,原因在于series中允許標簽名重復、dataframe中則允許列名和標簽名均有重復,而這是一個真正字典所不允許的。
考慮series和dataframe兼具Numpy數組和字典的特性,那么就不難理解二者的以下屬性:
ndim/shape/dtypes/size/T,分別表示了數據的維數、形狀、數據類型和元素個數以及轉置結果。其中,由于pandas允許數據類型是異構的,各列之間可能含有多種不同的數據類型,所以dtype取其復數形式dtypes。與此同時,series因為只有一列,所以數據類型自然也就只有一種,pandas為了兼容二者,series的數據類型屬性既可以用dtype也可以用dtypes獲取;而dataframe則只能用dtypes。index/columns/values,分別對應了行標簽、列標簽和數據,其中數據就是一個格式向上兼容所有列數據類型的array。為了沿襲字典中的訪問習慣,還可以用keys()訪問標簽信息,在series返回index標簽,在dataframe中則返回columns列名;可以用items()訪問鍵值對,但一般用處不大。這里提到了index和columns分別代表行標簽和列標簽,就不得不提到pandas中的另一個數據結構:Index,例如series中標簽列、dataframe中行標簽和列標簽均屬于這種數據結構。
既然是數據結構,就必然有數據類型dtype屬性,例如數值型、字符串型或時間類型等,其類型絕大多數場合并不是我們關注的主體,但有些時候值得注意,如后文中提到的通過[ ]執行標簽切片訪問行的過程。
此外,index數據結構還有名字屬性name(默認為None)、形狀屬性shape等。
關于series和dataframe數據結構本身,有大量的方法可用于重構結構信息:
rename,可以對標簽名重命名,也可以重置index和columns的部分標簽列信息,接收標量(用于對標簽名重命名)或字典(用于重命名行標簽和列標簽)reindex,接收一個新的序列與已有標簽列匹配,當原標簽列中不存在相應信息時,填充NAN或者可選的填充值set_index/reset_index,互為逆操作,前者是將已有的一列信息設置為標簽列,而后者是將原標簽列歸為數據,并重置為默認數字標簽set_axis,設置標簽列,一次只能設置一列信息,與rename功能相近,但接收參數為一個序列更改全部標簽列信息(rename中是接收字典,允許只更改部分信息)rename_axis,重命名標簽名,rename中也可實現相同功能在pandas早些版本中,除一維數據結構series和二維數據結構dataframe外,還支持三維數據結構panel。這三者是構成遞進包容關系,panel即是dataframe的容器,用于存儲多個dataframe。
2019年7月,隨著pandas 0.25版本的推出,pandas團隊宣布正式棄用panel數據結構,而相應功能建議由多層索引實現。
也正因為pandas這3種獨特的數據結構,個人一度認為pandas包名解釋為:pandas = panel + dataframe + series,根據維數取相應的首字母個數,從而構成pandas,這是個人非常喜歡的一種關于pandas縮寫的解釋。
03 數據讀寫
pandas支持大部分的主流文件格式進行數據讀寫,常用格式及接口為:
文本文件,主要包括csv和txt兩種等,相應接口為read_csv()和to_csv(),分別用于讀寫數據Excel文件,包括xls和xlsx兩種格式均得到支持,底層是調用了xlwt和xlrd進行excel文件操作,相應接口為read_excel()和to_excel()SQL文件,支持大部分主流關系型數據庫,例如MySQL,需要相應的數據庫模塊支持,相應接口為read_sql()和to_sql()此外,pandas還支持html、json等文件格式的讀寫操作。
04 數據訪問
series和dataframe兼具Numpy數組和字典的結構特性,所以數據訪問都是從這兩方面入手。同時,也支持bool索引進行數據訪問和篩選。
[ ],這是一個非常便捷的訪問方式,不過需區分series和dataframe兩種數據結構理解:series:既可以用標簽也可以用數字索引訪問單個元素,還可以用相應的切片訪問多個值,因為只有一維信息,自然毫無懸念dataframe:無法訪問單個元素,只能返回一列、多列或多行:單值或多值(多個列名組成的列表)訪問時按列進行查詢,單值訪問不存在列名歧義時還可直接用屬性符號" . "訪問。切片形式訪問時按行進行查詢,又區分數字切片和標簽切片兩種情況:當輸入數字索引切片時,類似于普通列表切片;當輸入標簽切片時,執行范圍查詢(即無需切片首末值存在于標簽列中),包含兩端標簽結果,無匹配行時返回為空,但要求標簽切片類型與索引類型一致。例如,當標簽列類型(可通過df.index.dtype查看)為時間類型時,若使用無法隱式轉換為時間的字符串作為索引切片,則引發報錯▲切片形式返回行查詢,且為范圍查詢
▲切片類型與索引列類型不一致時,引發報錯
loc/iloc,最為常用的兩種數據訪問方法,其中loc按標簽值訪問、iloc按數字索引訪問,均支持單值訪問或切片查詢。與[ ]訪問類似,loc按標簽訪問時也是執行范圍查詢,包含兩端結果at/iat,loc和iloc的特殊形式,不支持切片訪問,僅可以用單個標簽值或單個索引值進行訪問,一般返回標量結果,除非標簽值存在重復isin/notin,條件范圍查詢,即根據特定列值是否存在于指定列表返回相應的結果where,仍然是執行條件查詢,但會返回全部結果,只是將不滿足匹配條件的結果賦值為NaN或其他指定值,可用于篩選或屏蔽值query,按列對dataframe執行條件查詢,一般可用常規的條件查詢替代get,由于series和dataframe均可以看做是類字典結構,所以也可使用字典中的get()方法,主要適用于不確定數據結構中是否包含該標簽時,與字典的get方法完全一致lookup,loc的一種特殊形式,分別傳入一組行標簽和列標簽,lookup解析成一組行列坐標,返回相應結果:pandas中支持大量的數據訪問接口,但萬變不離其宗:只要聯想兩種數據結構兼具Numpy數組和字典的雙重特性,就不難理解這些數據訪問的邏輯原理。當然,重點還是掌握[]、loc和iloc三種方法。
loc和iloc應該理解為是series和dataframe的屬性而非函數,應用loc和iloc進行數據訪問就是根據屬性值訪問的過程。
另外,在pandas早些版本中,還存在loc和iloc的兼容結構,即ix,可混合使用標簽和數字索引,但往往容易混亂,所以現已棄用。
05 數據處理
pandas最為強大的功能當然是數據處理和分析,可獨立完成數據分析前的絕大部分數據預處理需求。簡單歸納來看,主要可分為以下幾個方面:
1. 數據清洗
數據處理中的清洗工作主要包括對空值、重復值和異常值的處理:
空值判斷空值,isna或isnull,二者等價,用于判斷一個series或dataframe各元素值是否為空的bool結果。需注意對空值的界定:即None或numpy.nan才算空值,而空字符串、空列表等則不屬于空值;類似地,notna和notnull則用于判斷是否非空填充空值,fillna,按一定策略對空值進行填充,如常數填充、向前/向后填充等,也可通過inplace參數確定是否本地更改刪除空值,dropna,刪除存在空值的整行或整列,可通過axis設置,也包括inplace參數重復值檢測重復值,duplicated,檢測各行是否重復,返回一個行索引的bool結果,可通過keep參數設置保留第一行/最后一行/無保留,例如keep=first意味著在存在重復的多行時,首行被認為是合法的而可以保留刪除重復值,drop_duplicates,按行檢測并刪除重復的記錄,也可通過keep參數設置保留項。由于該方法默認是按行進行檢測,如果存在某個需要需要按列刪除,則可以先轉置再執行該方法異常值,判斷異常值的標準依賴具體分析數據,所以這里僅給出兩種處理異常值的可選方法刪除,drop,接受參數在特定軸線執行刪除一條或多條記錄,可通過axis參數設置是按行刪除還是按列刪除替換,replace,非常強大的功能,對series或dataframe中每個元素執行按條件替換操作,還可開啟正則表達式功能2. 數值計算
由于pandas是在Numpy的基礎上實現的,所以Numpy的常用數值計算操作在pandas中也適用:
通函數ufunc,即可以像操作標量一樣對series或dataframe中的所有元素執行同一操作,這與Numpy的特性是一致的,例如前文提到的replace函數,本質上可算作是通函數。如下實現對數據表中逐元素求平方廣播機制,即當維度或形狀不匹配時,會按一定條件廣播后計算。由于pandas是帶標簽的數組,所以在廣播過程中會自動按標簽匹配進行廣播,而非類似Numpy那種純粹按順序進行廣播。例如,如下示例中執行一個dataframe和series相乘,雖然二者維度不等、大小不等、標簽順序也不一致,但仍能按標簽匹配得到預期結果字符串向量化,即對于數據類型為字符串格式的一列執行向量化的字符串操作,本質上是調用series.str屬性的系列接口,完成相應的字符串操作。尤為強大的是,除了常用的字符串操作方法,str屬性接口中還集成了正則表達式的大部分功能,這使得pandas在處理字符串列時,兼具高效和強力。例如如下代碼可用于統計每個句子中單詞的個數需注意的是,這里的字符串接口與Python中普通字符串的接口形式上很是相近,但二者是不一樣的。
時間類型向量化操作,如字符串一樣,在pandas中另一個得到"優待"的數據類型是時間類型,正如字符串列可用str屬性調用字符串接口一樣,時間類型列可用dt屬性調用相應接口,這在處理時間類型時會十分有效。3. 數據轉換
前文提到,在處理特定值時可用replace對每個元素執行相同的操作,然而replace一般僅能用于簡單的替換操作,所以pandas還提供了更為強大的數據轉換方法
map,適用于series對象,功能與Python中的普通map函數類似,即對給定序列中的每個值執行相同的映射操作,不同的是series中的map接口的映射方式既可以是一個函數,也可以是一個字典apply,既適用于series對象也適用于dataframe對象,但對二者處理的粒度是不一樣的:apply應用于series時是逐元素執行函數操作;apply應用于dataframe時是逐行或者逐列執行函數操作(通過axis參數設置對行還是對列,默認是行),僅接收函數作為參數applymap,僅適用于dataframe對象,且是對dataframe中的每個元素執行函數操作,從這個角度講,與replace類似,applymap可看作是dataframe對象的通函數。4. 合并與拼接
pandas中又一個重量級數據處理功能是對多個dataframe進行合并與拼接,對應SQL中兩個非常重要的操作:union和join。pandas完成這兩個功能主要依賴以下函數:
concat,與Numpy中的concatenate類似,但功能更為強大,可通過一個axis參數設置是橫向或者拼接,要求非拼接軸向標簽唯一(例如沿著行進行拼接時,要求每個df內部列名是唯一的,但兩個df間可以重復,畢竟有相同列才有拼接的實際意義)merge,完全類似于SQL中的join語法,僅支持橫向拼接,通過設置連接字段,實現對同一記錄的不同列信息連接,支持inner、left、right和outer4種連接方式,但只能實現SQL中的等值連接join,語法和功能與merge一致,不同的是merge既可以用pandas接口調用,也可以用dataframe對象接口調用,而join則只適用于dataframe對象接口append,concat執行axis=0時的一個簡化接口,類似列表的append函數一樣實際上,concat通過設置axis=1也可實現與merge類似的效果,二者的區別在于:merge允許連接字段重復,類似一對多或者多對一連接,此時將產生笛卡爾積結果;而concat則不允許重復,僅能一對一拼接。
▲建表語句
▲通過設置參數,concat和merge實現相同效果
06 數據分析
pandas中的另一大類功能是數據分析,通過豐富的接口,可實現大量的統計需求,包括Excel和SQL中的大部分分析過程,在pandas中均可以實現。
1. 基本統計量
pandas內置了豐富的統計接口,這是與Numpy是一致的,同時又包括一些常用統計信息的集成接口。
info,展示行標簽、列標簽、以及各列基本信息,包括元素個數和非空個數及數據類型等head/tail,從頭/尾抽樣指定條數記錄describe,展示數據的基本統計指標,包括計數、均值、方差、4分位數等,還可接收一個百分位參數列表展示更多信息count、value_counts,前者既適用于series也適用于dataframe,用于按列統計個數,實現忽略空值后的計數;而value_counts則僅適用于series,執行分組統計,并默認按頻數高低執行降序排列,在統計分析中很有用unique、nunique,也是僅適用于series對象,統計唯一值信息,前者返回唯一值結果列表,后者返回唯一值個數(number of unique)sort_index、sort_values,既適用于series也適用于dataframe,sort_index是對標簽列執行排序,如果是dataframe可通過axis參數設置是對行標簽還是列標簽執行排序;sort_values是按值排序,如果是dataframe對象,也可通過axis參數設置排序方向是行還是列,同時根據by參數傳入指定的行或者列,可傳入多行或多列并分別設置升序降序參數,非常靈活。另外,在標簽列已經命名的情況下,sort_values可通過by標簽名實現與sort_index相同的效果。2. 分組聚合
pandas的另一個強大的數據分析功能是分組聚合以及數據透視表,前者堪比SQL中的groupby,后者媲美Excel中的數據透視表。
groupby,類比SQL中的group by功能,即按某一列或多列執行分組。一般而言,分組的目的是為了后續的聚合統計,所有groupby函數一般不單獨使用,而需要級聯其他聚合函數共同完成特定需求,例如分組求和、分組求均值等。▲pandas官網關于groupby過程的解釋
級聯其他聚合函數的方式一般有兩種:單一的聚合需求用groupby+聚合函數即可,復雜的大量聚合則可借用agg函數,agg函數接受多種參數形式作為聚合函數,功能更為強大。
▲兩種分組聚合形式
pivot,pivot英文有"支點"或者"旋轉"的意思,排序算法中經典的快速排序就是不斷根據pivot不斷將數據二分,從而加速排序過程。用在這里,實際上就是執行行列重整。例如,以某列取值為重整后行標簽,以另一列取值作為重整后的列標簽,以其他列取值作為填充value,即實現了數據表的行列重整。以SQL中經典的學生成績表為例,給定原始學生—課程—成績表,需重整為學生vs課程的成績表,則可應用pivot實現:另外,還有一對函數也常用于數據重整,即stack和unstack,其中unstack執行效果與pivot非常類似,而stack則是unstack的逆過程。
pivot_table,有了pivot就不難理解pivot_table,實際上它是在前者的基礎上增加了聚合的過程,類似于Excel中的數據透視表功能。仍然考慮前述學生成績表的例子,但是再增加一列班級信息,需求是統計各班級每門課程的平均分。由于此時各班的每門課成績信息不唯一,所以直接用pivot進行重整會報錯,此時即需要對各班各門課程成績進行聚合后重整,比如取平均分。07 數據可視化
pandas集成了Matplotlib中的常用可視化圖形接口,可通過series和dataframe兩種數據結構面向對象的接口方式簡單調用。
兩種數據結構作圖,區別僅在于series是繪制單個圖形,而dataframe則是繪制一組圖形,且在dataframe繪圖結果中以列名為標簽自動添加legend。另外,均支持兩種形式的繪圖接口:
plot屬性+相應繪圖接口,如plot.bar()用于繪制條形圖plot()方法并通過傳入kind參數選擇相應繪圖類型,如plot(kind='bar')不過,pandas繪圖中僅集成了常用的圖表接口,更多復雜的繪圖需求往往還需依賴Matplotlib或者其他可視化庫。