在進行數據分析之前,我們需要做的事情是對數據有初步的了解,比如對數據本身的敏感程度,通俗來說就是對數據的分布有大概的理解,此時我們需要工具進行數據的描述,觀測數據的形狀等;而后才是對數據進行建模分析,挖掘數據中隱藏的位置信息。怒氣按在數據描述和簡單分析方面做得比較好的是Pandas庫。當然,它還需要結合Numpy。Scipy等科學計算相關庫才能發揮功效。
Pandas數據結構
在進行Pandas相關介紹時,我們首先需要知道的是Pandas的兩個數據結構(即對象)Series和DataFrame,這是Pandas的核心結構,掌握了此二者結構和屬性要素,會在具體的數據處理過程中如虎添翼。
1)Series簡介
Series是一種類似於一維數組的對象,它由兩部分組成,第一部分是一維數據,另外一部分是與此一維數據對應的標簽數據。但是語言描述不好理解,具體如下:
import pandas as pd #這是約定俗成的寫法,一般而言,大家都會寫pd,當然也可以換成別的 centerSeries=pd.Series(['中國科學院','文獻情報中心','大樓','北四環西路']); print(centerSeries); #output: # 0 中國科學院 # 1 文獻情報中心 # 2 大樓 # 3 北四環西路 # dtype: object
因為我們沒有指定標簽數據,而Python默認是通過數字排序進行標識,接下來給它添加標識數據。
import pandas as pd #這是約定俗成的寫法,一般而言,大家都會寫pd,當然也可以換成別的 centerSeries=pd.Series(['中國科學院','文獻情報中心','大樓','北四環西路'],index=['a','b','c','d']); # index的size同Series必須一樣大,否則報錯 print(centerSeries); #output: # a 中國科學院 # b 文獻情報中心 # c 大樓 # d 北四環西路 # dtype: object
對比之前的默認標識,我們可以看出它是由1,2,3,4變成了a,b,c,d。接下來將解釋這樣標識的意義。
import pandas as pd #這是約定俗成的寫法,一般而言,大家都會寫pd,當然也可以換成別的 centerSeries=pd.Series(['中國科學院','文獻情報中心','大樓','北四環西路'],index=['a','b','c','d']); # index的size同Series必須一樣大,否則報錯 print(centerSeries[0])#通過一維數組進行獲取數據 print(centerSeries[1]) print(centerSeries['c'])#通過標識index獲取數據 print(centerSeries['d']) # output: # 中國科學院 # 文獻情報中心 # 大樓 # 北四環西路
另外,我們可以看到通過一位數組格式獲取數據和通過index標識獲取數據都可以,這樣的index就像曾經學過的數據庫中的id列的作用,相當於建立了每個數據的索引。當然,針對Series的操作不只限於此,還有很多需要我們自己去通過“help”查看得到的。
2)DataFrame簡介
DataFrame是一個表格型的數據結構,它包含有列和行索引,當然你也可以把它看做是由Series組織成的字典。需要說明的是,DataFrame的每一列中不需要數據類型相同,且它的數據是通過一個或者多個二維塊進行存放,在了解DataFrame之前如果你對層次化索引有所了解,那么DataFrame可能相對容易理解,當然不知道也沒有關系,舉個簡單的例子:它類似於常見的excel表格格式,可將它理解為一張excel表,至於DataFrame在內部具體如何處理,這個作為應用層的過程我們先不討論。
import pandas as pd # 簡單的DataFrame制作 # 字典格式的數據 data={'name':['國科圖','國科圖','文獻情報中心','文獻情報中心'], 'year':['2012','2013','2014','2015'], 'local':['北四環西路','北四環西路','北四環西路','北四環西路'], 'student':['甲','乙','丙','丁']} centerDF=pd.DataFrame(data); centerDF #同樣,默認的index是數字表示,而且它的列名也是按字母順序排序的
運行結果:
當然我們可以調整列的格式,index仍采用默認:
在pd.DataFrame()函數中明確指定參數columns
centerDF=pd.DataFrame(data,columns=['year','name','local','student']);
結果:
更改index的默認設置:
在pd.DataFrame()函數中明確指定參數index
centerDF=pd.DataFrame(data,columns=['year','name','local','student'],index=['a','b','c','d']);
既然DataFrame是行列格式的數據,那么理所當然可以通過行、列的方式進行數據獲取,按列進行數據獲取:
import pandas as pd # 簡單的DataFrame制作 # 字典格式的數據 data={'name':['國科圖','國科圖','文獻情報中心','文獻情報中心'], 'year':['2012','2013','2014','2015'], 'local':['北四環西路','北四環西路','北四環西路','北四環西路'], 'student':['甲','乙','丙','丁']} centerDF=pd.DataFrame(data,columns=['year','name','local','student'],index=['a','b','c','d']); print(centerDF['name']); print() print('列的類型:',type(centerDF['name'])) print(); print(centerDF['year']) # output: # a 國科圖 # b 國科圖 # c 文獻情報中心 # d 文獻情報中心 # Name: name, dtype: object # 列的類型: <class 'pandas.core.series.Series'> # a 2012 # b 2013 # c 2014 # d 2015 # Name: year, dtype: object
另外,可以看出按列進行獲取時它們的index表示是相同的,且每一列是一個Series對象。
按行進行數據獲取,其實是通過index進行操作。
import pandas as pd # 簡單的DataFrame制作 # 字典格式的數據 data={'name':['國科圖','國科圖','文獻情報中心','文獻情報中心'], 'year':['2012','2013','2014','2015'], 'local':['北四環西路','北四環西路','北四環西路','北四環西路'], 'student':['甲','乙','丙','丁']} centerDF=pd.DataFrame(data,columns=['year','name','local','student'],index=['a','b','c','d']); print(centerDF.loc['a']); print() print('行的類型:',type(centerDF.loc['a'])) # output: #year 2012 # name 國科圖 # local 北四環西路 # student 甲 # Name: a, dtype: object # 行的類型: <class 'pandas.core.series.Series'>
其中‘loc’,也可用‘iloc’,不推薦使用‘ix’,因為其已經被棄用。
可以看出,每一行是一個Series對象,此時該Series的index其實就是DataFrame的列名稱,綜上來看,對於一個DataFrame來說,它是縱橫雙向進行索引,只是每個Series(縱橫)都共用一個索引而已。
3)利用Pandas加載、保存數據
在進行數據處理時我們首要的工作是把數據加載到內存中,這一度成為程序編輯的軟肋,但是Pandas包所提供的功能幾乎涵蓋了大多數的數據處理的加載問題,如read_csv、read_ExcelFile等。
1.加載、保存csv格式的數據
原文件:
代碼:
#! /usr/bin/env python #coding=utf-8 #加載csv格式的數據 data_csv=pd.read_csv(r'C:\Users\123\Desktop\result.csv');#它的默認屬性有sep=',' data_csv
運行結果:
更改默認屬性
data_csv=pd.read_csv(r'C:\Users\123\Desktop\result.csv',sep='#');#更改默認屬性有sep='#' data_csv
運行結果:
不要表頭:
data_csv=pd.read_csv(r'C:\Users\123\Desktop\result.csv',header=None,skiprows=[0]);#不要表頭Header data_csv
結果:
自行添加表頭列:
#! /usr/bin/env python #coding=utf-8 #加載csv格式的數據 data_csv=pd.read_csv(r'C:\Users\123\Desktop\result.csv',header=None,skiprows=[0]);#不要表頭Header print(type(data_csv))#通過它的類型我們可以看到它是DataFrame #可自行添加表頭列 data_csv.columns=['第一列','第二列','第三列','第四列','第五列','第六列','第七列','第八列','第十列','第十一列','第十二列','第十三列']; data_csv
運行結果:
保存csv數據:
#! /usr/bin/env python #coding=utf-8 #加載csv格式的數據 data_csv=pd.read_csv(r'C:\Users\123\Desktop\result.csv',header=None,skiprows=[0]);#不要表頭Header print(type(data_csv))#通過它的類型我們可以看到它是DataFrame #可自行添加表頭列 data_csv.columns=['第一列','第二列','第三列','第四列','第五列','第六列','第七列','第八列','第十列','第十一列','第十二列','第十三列']; data_csv.loc[0,'第一列']='新添加內容'; data_csv.to_csv(r'C:\Users\123\Desktop\result2.csv'); data_csv
運行結果:
生成新文件:
綜上所述,通過對csv格式的文件進行讀取,我們可以指定讀入的格式(sep=','),也可以指定它的header為空None,最后添加column,而之所以可以后來添加的原因是讀入的csv已經是DataFrame對象了。
2.加載、保存excel格式的數據
# 讀取excel文件 data_excel=pd.read_excel(r'result.xlsx',encoding='utf-8',sheetname='Sheet1'); #保存數據 data_excel.to_excel(r'result2.xlsx',sheet_name='Sheet1');
對於excel文件來說,同csv格式的處理相差無幾,但是excel文件在處理時需要指定sheetname屬性(讀取和寫入sheet_name)。
3.加載、保存json格式的數據
將json的數據格式也在此說明是因為json數據通常是系統之間數據交互的定制化xml格式的數據,現在它已經成為web系統進行數據交互的默認標准。
import json #源數據(真實的json格式的數據多數是通過http傳輸過來的) obj={'school':'UCAS','institute':'NSLCAS','name':['tovi','karen','jack']}; # 讀取json obj_json=json.dumps(obj) data_json=json.loads(obj_json) print(data_json) print(type(data_json)) # output: # {'school': 'UCAS', 'institute': 'NSLCAS', 'name': ['tovi', 'karen', 'jack']} # <class 'dict'>
我們可以看到讀入的json數據被轉換成了字典。
關於json數據的保存,實際來說只是在數據交換處理中的一種格式,真正的保存沒有實際意義,因為真實的場景是進行數據交換,處理完的json數據會做后續的處理。
利用Pandas處理數據
一、匯總計算
當我們知道如何加載數據后,接下來就是如何處理數據,雖然之前的賦值計算也是一種計算,但是如果Pandas的作用就停留在此,那我們也許只是看到了它的冰山一角,它首先比較吸引人的作用是匯總計算。
1)基本的數學統計計算
這里的基本計算指的是sum、mean等操作,主要是基於Series(也可能是來自DataFrame)進行統計計算。
代碼1:
#統計計算sum、mean等 import numpy as np import pandas as pd df=pd.DataFrame(np.arange(16).reshape((4,4)),columns=['aa','bb','cc','dd'],index=['a','b','c','d']) print(df) # output: # aa bb cc dd # a 0 1 2 3 # b 4 5 6 7 # c 8 9 10 11 # d 12 13 14 15
代碼2:
df_data=df.reindex(['a','b','c','d','e']); df_data #數據中既有正常值,也有NaN值
結果:
代碼3:
df_data.sum()#默認是通過列進行求和,即axis=0;默認NaN值也是忽略的
結果:
代碼4:
df_data.sum(axis=1)#改為通過行進行求和
結果:
代碼5:
df_data.mean()#NaN值默認忽略
同樣的sum()函數也是NaN值默認忽略。
結果:
代碼6:
df_data.mean(axis=0,skipna=False) #對於NaN不跳過
結果:
代碼7:
#idxmax idxmin最大值、最小值的索引 print(df.idxmax()) print(df.idxmin()) # output: # aa d # bb d # cc d # dd d # dtype: object # aa a # bb a # cc a # dd a # dtype: object
代碼8:
#進行累計cumsum print(df.cumsum())
代碼9:
#對於剛才提到的大多數描述性統計可以使用describe df_data.describe()
結果:
代碼10:
#對於這些統計量的含義可以查找“help”得到,此處不再贅述 help()
2)唯一值、值的計數、成員資格的設定
采用幾行代碼、一個output進行演示:
import pandas as pd #是否是唯一值 obj=pd.Series(['a','a','b','b','b','c','c']) print(obj) # output: # 0 a # 1 a # 2 b # 3 b # 4 b # 5 c # 6 c # dtype: object print(obj.unique()) # output: # ['a' 'b' 'c'] # value_counts是Python針對Series進行的頂級操作 print(pd.value_counts(obj.values,sort=False)) # output: # a 2 # b 3 # c 2 # dtype: int64 mark=obj.isin(['a'])#是否存在a print(mark) # output: # 0 True # 1 True # 2 False # 3 False # 4 False # 5 False # 6 False # dtype: bool obj[mark]#根據判定條件進行數據獲取 # output: # 0 a # 1 a # dtype: object
另外,實際應用中不只是這些統計函數在發揮作用,還有很多統計函數,比如計算數值之間的百分比變化(pct_change),或者是相關數據的系數與協方差等,這里就不做討論了,需要時可查看幫助文檔來解決。
二、缺失值處理
1)缺失值概念
缺失值(missing data)是在數據處理中在所難免的問題,Pandas對缺失值的處理目的是簡化對缺失值處理的工作。缺失值在Pandas中使用的是浮點數(numpy.nan:Not a Number)。
代碼1:
data=pd.Series([11,22,33,np.nan,55]);#定義NaN值通過numpy.nan data # output: # 0 11.0 # 1 22.0 # 2 33.0 # 3 NaN # 4 55.0 # dtype: float64
代碼2:
data.isnull()#判定是否為空NaN # output: # 0 False # 1 False # 2 False # 3 True # 4 False # dtype: bool
代碼3:
#Python 中對於None也認為是NaN data[2]=None; data # output: # 0 11.0 # 1 22.0 # 2 NaN # 3 NaN # 4 55.0 # dtype: float64
2)過濾缺失值
對於缺失值的過濾主要通過dropna進行。
代碼1:
data.dropna()#過濾掉NaN值 # output: # 0 11.0 # 1 22.0 # 4 55.0 # dtype: float64
代碼2:
#當然drop太過暴力——它會過濾點所有的NaN值,這樣往往不是一般正常需要的處理結果 #我們可以通過dropna的屬性進行限定 df=pd.DataFrame(np.arange(16).reshape(4,4),columns=['aa','bb','cc','dd'],index=['a','b','c','d']) #制造NaN值 df.loc[:1,:]=np.nan print(df) # output: # aa bb cc dd # a NaN NaN NaN NaN # b 4.0 5.0 6.0 7.0 # c 8.0 9.0 10.0 11.0 # d 12.0 13.0 14.0 15.0 print(df.dropna(axis=1,how='all'))#0行1列 # 並沒有什么變化,因為過濾的是列,要求一列全都是NaN值 # output: # aa bb cc dd # a NaN NaN NaN NaN # b 4.0 5.0 6.0 7.0 # c 8.0 9.0 10.0 11.0 # d 12.0 13.0 14.0 15.0 print(df.dropna(axis=0,how='all'))#0行1列 # output: # aa bb cc dd # b 4.0 5.0 6.0 7.0 # c 8.0 9.0 10.0 11.0 # d 12.0 13.0 14.0 15.0
3)填充缺失值
因為數據處理的要求,可能並不需要將所有數據進行過濾,此時需要對數據進行必要的填充(比如0.0);還可以用線性插值進行必要的填充,而這個在數據處理中經常需要用到的方式如下:
df=pd.DataFrame(np.arange(16).reshape(4,4),columns=['aa','bb','cc','dd'],index=['a','b','c','d']) #制造NaN值 df.loc[:1,:]=np.nan print(df) # output: # aa bb cc dd # a NaN NaN NaN NaN # b 4.0 5.0 6.0 7.0 # c 8.0 9.0 10.0 11.0 # d 12.0 13.0 14.0 15.0 print(df.fillna(0.0))#fillna默認會返回新的對象 #也可以像dropna操作一樣進行必要的限定而不是所有的值都進行填充 # print(df.fillna({1:0.5,2:5.5}))#測試失敗 #當需要在舊的對象上進行更改,而不是經過過濾返回一個新的對象時 df.fillna(0.5,inplace=True) print(df) # output: # aa bb cc dd # a 0.5 0.5 0.5 0.5 # b 4.0 5.0 6.0 7.0 # c 8.0 9.0 10.0 11.0 # d 12.0 13.0 14.0 15.0 #可以選擇一些線性插值進行填充 df.loc[:1,:]=np.nan #后向尋值填充 print(df.fillna(method='bfill')) # output: # aa bb cc dd # a 4.0 5.0 6.0 7.0 # b 4.0 5.0 6.0 7.0 # c 8.0 9.0 10.0 11.0 # d 12.0 13.0 14.0 15.0 print(df.fillna(df.mean()))#使用平均值進行填充 # output: # aa bb cc dd # a 8.0 9.0 10.0 11.0 # b 4.0 5.0 6.0 7.0 # c 8.0 9.0 10.0 11.0 # d 12.0 13.0 14.0 15.0
另外,在處理缺失值時除了以上介紹的簡單操作之外,更多的時候需要根據數據挖掘需求或者程序運行方面靈活地進行缺失值處理,程序是認為設定的規則,但針對這些規則進行優化組合將會帶來新的效果。
參考書目:《數據館員的Python簡明手冊》