Python有很多典型應用,我們日常工作經常遇到一些數據分析相關的問題,傳統的方式使用Excel進行處理,耗時長,特定是一些重復性的例行工作。
Python提供很多強大的數據分析相關的庫,如numpy、pandas等。其實我們簡單的日常使用pandas就夠了。
Pandas介紹
Pandas是一個強大的數據分析第三方庫,Anaconda已經自動攜帶,無需安裝,只需import導入即可使用。一般用以下語法:
import pandas as pd
Pandas提供兩種常用的數據結構:Series和DataFrame。
其中Series可以看作一行或一列數據,DataFrame是一個二維表格數據,和excel表格類似,有行索引和列索引。
Pandas提供很多便捷的函數,用來創建、處理、保存DataFrame,任何你想到的遇到的問題都可以百度解決。
建議通過十分鐘入門Pandas進行快速了解,當然有作者翻譯成了中文版,自己搜索。
下面通過一個實際案例詳細了解Pandas進行數據分析處理的過程。
問題需求
在日常工作中,我們需要定期從某個網站下載數據表格。
我們需要將多份表格中的 “廠家-設備類型” Sheet數據合并、處理、匯總,該sheet的數據格式如下:
我們期望按照廠家、設備類型匯報告警量,由于部分廠家、設備類型可能缺失導致無法對齊,無法自動相加,人工匯總困難,而python則非常簡單。
代碼實現
首先使用pandas讀取兩個excel,分別df1和df2,然后使用pandas自帶的合并函數實現匯總,自動對齊、處理缺失值。
pandas讀取excel
import pandas as pd
#定義待讀取的文件名
filename1 = '省監控-核心網-全量告警_38AB1AE7.xlsx'
filename2 = '省監控-核心網-全量告警_964C9DCF.xlsx'
#使用pandas的函數讀取excel,當前目錄,直接寫文件名即可,可以有很多參數,這里指定所需sheet
df1 = pd.read_excel(filename1,sheetname='廠家-設備類型')
df2 = pd.read_excel(filename2,sheetname='廠家-設備類型')
以上代碼可以讀取兩個excel文件。可以通過pandas.head() 可以顯示前幾行數據,
可以快速查看DataFrame的格式,如列名,數據格式等,判斷是否正確載入數據。
代碼的關鍵是掌握pandas.read_excel()函數。
pandas.read_excel(io, sheet_name=0, header=0, skiprows=None, skip_footer=0, index_col=None, names=None, usecols=None, parse_dates=False, date_parser=None, na_values=None, thousands=None, convert_float=True, converters=None, dtype=None, true_values=None, false_values=None, engine=None, squeeze=False, **kwds)
其中io或者filename就是待讀取的文件路徑,可以是相對路徑或絕對路徑。sheetname指定sheet,還有很多參數可以個性化讀取文件,這里無需使用。
同時pandas還提供了大量其他read類函數,可以讀取其他類型文件,如sql、csv等等。
pandas按照特定列合并處理
那么這兩個df如何按照廠家和設備類型對告警量進行累加呢,也就是按照“廠家”和“設備類型”兩列進行處理。
有一個最直觀的辦法就是循環,通過遍歷所有廠家和設備類型,然后講兩個df對應的值求和,,雖然想法很簡單,但是實現還是很復雜。
首先的知道兩個文件的最大廠家、設備類型集合,然后再遍歷。
#獲取兩個文件的最大廠家、設備類型集合
cjlx1 = [tuple(df1.loc[i][['廠家','設備類型']]) for i in df1.index]
cjlx2 = [tuple(df2.loc[i][['廠家','設備類型']]) for i in df2.index]
cjlx = set(cjlx1+cjlx2)
#遍歷最大設備類型集合,求得合并告警量
rows = [] #輸出列表集合,可以轉換為DataFrame
for i in cjlx: #遍歷最大(廠家、類型)集合
cj = i[0] #獲取廠家名稱
lx = i[1] #獲取設備類型
if i in cjlx1: #如果該(廠家,類型)對在文件1中,取得對應告警量,否則告警量為0
num1 = df1[(df1['廠家'] == cj) & (df1['設備類型']==lx)]['告警量'].iloc[0]
else:
num1 = 0
if i in cjlx2:
num2 = df2[(df2['廠家'] == cj) & (df2['設備類型']==lx)]['告警量'].iloc[0]
else:
num2 = 0
num = num1 + num2 #兩個告警量相加
row = [cj,lx,num] #生成新的一行數據
rows.append(row) #追加到輸出列表
data = pd.DataFrame(rows,columns = ['廠家','設備類型','告警量']) #轉換為DataFrame
可以看到最新的data已經是匯總后的數據,一個excel有71行數據,一個78行數據,合并后數據80行。
In[112]: len(df1),len(df2),len(data) #In表示輸入,冒號后才是真實代碼
Out[112]: (71, 78, 80)
In[113]:data.head()
Out[113]:
廠家 設備類型 告警量
0 CISCO 交換機 2021
1 東信 HOST 56
2 東信 交換機 16
3 中興 HOST 182
4 愛立信 HSS_SLF 2
上面代碼涉及了Python的常用語法和Pandas的基本操作,可以簡單分享下,更多內容需要自己查閱。
- python list的基本操作
創建[1,2,3],append,list1+list2,列表表達式創建新list [ i for i in x] 可以對i進行操作變換,可以加條件過濾,如
In[114]:[i*2 for i in range(5) if i >2] #In表示輸入,冒號后才是真實代碼
Out[114]: [6, 8]
In[115]:[i*2 for i in range(5) ]
Out[115]: [0, 2, 4, 6, 8]
還有Python的集合set、元組tuple的基礎操作,建議自學。
- for if基本控制流
通過for循環遍歷,用in關鍵字實現遍歷,if...else實現條件判斷,最簡單示例如下:
for i in range(5):
print(i)
if i>2:
print('我是if為真的結果:',i*2)
0
1
2
3
我是if為真的結果: 6
4
我是if為真的結果: 8
- Pandas的基本操作
DataFrame可以遍歷索引index,可以快速選取某些值。
.loc 可以獲取某個位置值,df[bool] 可以快速選擇bool為真的那些行數據。
如何快速選取某些行或某些列。如
In[118]:df1.loc[0][['廠家','設備類型']]
Out[118]:
廠家 CISCO
設備類型 HOST
Name: 0, dtype: object
In[119]:df1.loc[0]['廠家']
Out[119]: 'CISCO'
In[121]: bl = df1['廠家'] == cj
In[122]: len(bl),bl[0] #得到長度為71的bool值list
Out[122]: (71, False)
In[123]:df1[bl] #獲取bl為真的那些行數據
Out[123]:
廠家 設備類型 告警量
7 愛立信 BSC 254984
8 愛立信 CG 175
9 愛立信 HLR_FE 4100
更多內容關注Pandas官網或者其他相關教程。
Pandas優化處理
Pandas是一個非常優秀的數據處理庫,實現上述功能肯定不用這么復雜,有一些自帶的函數可以快速合并、規整兩個DataFrame。主要有append、merge和concat等操作。
- append
可以在df后添加行或者另一個df,有一些限制。然后對df3進行分組groupby,對告警量列進行求和即可(分組建會作為index,需要提取出來作為新的一列):
In[125]:df3 = df1.append(df2)
In[125]:len(df1),len(df2),len(df3) #df3的行數是df1 和df2的和
Out[125]: (71, 78, 149)
- merge
通過一個或多個鍵(列名)將行連接起來。多種連接方式,左連接,右連接等。
其中on表示用哪些鍵連接起來,how表示連接方式。
In[135]:df4 = pd.merge(df1,df2,on=['廠家','設備類型'],how = 'outer')
In[136]:df1.shape,df2.shape,df4.shape, #可以發現df4是80*4,其中80行已經是最大集合,是我們想要的結果,
Out[136]: ((71, 3), (78, 3), (80, 4))
In[137]:df4.head()
Out[137]:
廠家 設備類型 告警量_x 告警量_y
0 CISCO HOST 16.0 1.0
1 CISCO 交換機 1484.0 537.0
2 CISCO 路由器 93.0 152.0
3 IBM HOST 702.0 745.0
4 JUNIPER 路由器 7.0 6.0
其中告警量_x,告警量_y是原先兩個告警量,重名會自動增加_x和_y,以便區分。
接下來只需將這兩列相加即可。
df4['告警量'] = df4['告警量_x'] + df4['告警量_y']
這樣直接相加會有問題,存在nan值問題。
因此我們需要將nan值替換為0,再求和。修改后代碼如下
#使用pd.merge() 快速連接
data = pd.merge(df1,df2,on=['廠家','設備類型'],how = 'outer') #連接兩個df
data = data.fillna(0) #用0替換nan值
data['告警量'] = data['告警量_x'] + data['告警量_y'] #兩個告警量相加,得到新的一列告警量
data = data[['廠家','設備類型','告警量']] #只選取我們想要的三列
以上生成的data和一開始循環遍歷結果一樣,但是代碼非常精簡。同樣concat可以實現類似的連接,然后再進行處理。請自行實現。
Pandas文件保存
Pandas可以非常方便將文件保存為各種格式,如df.to_csv()、df.to_excel()。建議直接使用to_csv,簡單快速。
data.to_csv('out.csv',encoding= 'gbk',index = False)
ls
C:\Users\zhuf0\Documents\repository\pythondemo 的目錄
2018/04/04 08:44 <DIR> .
2018/04/04 08:44 <DIR> ..
2018/04/04 08:44 521 out.csv
可以看到本地目錄已經有out.csv。其中encoding設置了編碼方式、index可以設置是否保存索引。還有更多參數關注官網及其他教程。
代碼優化
上面已經實現了核心功能,下面將代碼優化一下,以便更好用。
優化1 基本功能函數化
編寫一個函數,輸入兩個df,返回求和后df。
def get_df_sums(df1,df2):
if df1.empty: #檢查其中一個df為空
return df2
elif df2.empty:
return df1
else:
data = pd.merge(df1,df2,on=['廠家','設備類型'],how = 'outer') #連接兩個df
data = data.fillna(0) #用0替換nan值
data['告警量'] = data['告警量_x'] + data['告警量_y'] #兩個告警量相加,得到新的一列告警量
data = data[['廠家','設備類型','告警量']] #只選取我們想要的三列
return data
優化2 自動讀取多個文件
一般情況下,待匯總的文件不止兩個,我們可以使用Python腳本自動讀取某個特定路徑下所有文件。
import os
path = r"C:\Users\zhuf0\Documents\repository\pythondemo"
out = pd.DataFrame()
for filename in os.listdir(path):#遍歷指定路徑的所有文件名
if '省監控-核心網-全量告警' in filename: #選擇指定文件待讀取
filename = path+"\\"+filename #獲取絕對路徑
df1 = pd.read_excel(filename,sheetname='廠家-設備類型') #讀取該sheet
out = get_df_sums(out,df1) #和之前的out累積求和,類似 sum=sum+i
只需要將待匯總的文件放到指定目錄即可,輸出out.csv。
優化3 排序后保存
Pandas 有很強大的排序功能,可以按照多列排序。sort_values
如按照告警量排序:
In[164]out = out.sort_values(by = '告警量',ascending = False) #按照告警量降序排列
In[164]:out.head()
Out[164]:
廠家 設備類型 告警量
7 愛立信 BSC 481307.0
65 中興 MME 163725.0
14 愛立信 MME 99553.0
13 愛立信 MGW 37488.0
16 愛立信 MSC_Server 24683.0
留個問題,能否分組排序,按廠家分組,如愛立信,然后組內告警量降序排列。廠家的排序方式按照該廠家的最大告警量排序,而不是廠家的名稱。如愛立信后是中興。
最終代碼
為了更像一個腳本,也為了后續擴展復用,最終優化后腳本為,保存為pandas_demo.py文件:
import pandas as pd
import os
#給定兩個df,返回求和后結果
def get_df_sums(df1,df2):
if df1.empty: #檢查其中一個df為空
return df2
elif df2.empty:
return df1
else:
data = pd.merge(df1,df2,on=['廠家','設備類型'],how = 'outer') #連接兩個df
data = data.fillna(0) #用0替換nan值
data['告警量'] = data['告警量_x'] + data['告警量_y'] #兩個告警量相加,得到新的一列告警量
data = data[['廠家','設備類型','告警量']] #只選取我們想要的三列
return data
#給定路徑path,求和指定格式全部文件
def get_all_path(path):
out = pd.DataFrame()
for filename in os.listdir(path):#遍歷指定路徑的所有文件名
if '省監控-核心網-全量告警' in filename: #選擇指定文件待讀取
filename = path+"\\"+filename #獲取絕對路徑
df1 = pd.read_excel(filename,sheetname='廠家-設備類型') #讀取該sheet
out = get_df_sums(out,df1) #和之前的out累積求和,類似 sum=sum+i
out = out.sort_values(by = '告警量',ascending = False)
return out
#作為主程序運行
if __name__ =='__main__':
path = r"C:\Users\zhuf0\Documents\repository\pythondemo"
out = get_all_path(path)
out.to_csv('out.csv',encoding= 'gbk',index = False) #保存
腳本使用
只要我們將待處理的文件放到該目錄,然后命令行運行該腳本即可。
執行 python pandas_demo.py 沒有任何提示,說明成功。
打開out.csv查看
同樣我們可以進一步優化將路徑作為參數提供,指定輸出目錄,增加一些提示性輸出,增加腳本穩健性。
Python數據分析功能非常強大,網上也有很多很好的項目和教程,大家可以結合實際工作問題,選擇合適項目入門學習。
時間優先,沒有太多排版。如有錯誤請及時反饋。
下一篇我們學習用Python自動運維——簡明Python開發教程(4):網絡自動化運維的曙光