Python有很多典型應(yīng)用蔬崩,我們?nèi)粘9ぷ鹘?jīng)常遇到一些數(shù)據(jù)分析相關(guān)的問題恶座,傳統(tǒng)的方式使用Excel進(jìn)行處理,耗時(shí)長(zhǎng)沥阳,特定是一些重復(fù)性的例行工作跨琳。
Python提供很多強(qiáng)大的數(shù)據(jù)分析相關(guān)的庫,如numpy桐罕、pandas等脉让。其實(shí)我們簡(jiǎn)單的日常使用pandas就夠了。
Pandas介紹
Pandas是一個(gè)強(qiáng)大的數(shù)據(jù)分析第三方庫功炮,Anaconda已經(jīng)自動(dòng)攜帶溅潜,無需安裝,只需import導(dǎo)入即可使用死宣。一般用以下語法:
import pandas as pd
Pandas提供兩種常用的數(shù)據(jù)結(jié)構(gòu):Series和DataFrame伟恶。
其中Series可以看作一行或一列數(shù)據(jù),DataFrame是一個(gè)二維表格數(shù)據(jù)毅该,和excel表格類似博秫,有行索引和列索引潦牛。
Pandas提供很多便捷的函數(shù),用來創(chuàng)建挡育、處理巴碗、保存DataFrame,任何你想到的遇到的問題都可以百度解決即寒。
建議通過十分鐘入門Pandas進(jìn)行快速了解橡淆,當(dāng)然有作者翻譯成了中文版,自己搜索母赵。
下面通過一個(gè)實(shí)際案例詳細(xì)了解Pandas進(jìn)行數(shù)據(jù)分析處理的過程逸爵。
問題需求
在日常工作中,我們需要定期從某個(gè)網(wǎng)站下載數(shù)據(jù)表格凹嘲。
我們需要將多份表格中的 “廠家-設(shè)備類型” Sheet數(shù)據(jù)合并师倔、處理、匯總周蹭,該sheet的數(shù)據(jù)格式如下:
我們期望按照廠家趋艘、設(shè)備類型匯報(bào)告警量,由于部分廠家凶朗、設(shè)備類型可能缺失導(dǎo)致無法對(duì)齊瓷胧,無法自動(dòng)相加,人工匯總困難棚愤,而python則非常簡(jiǎn)單搓萧。
代碼實(shí)現(xiàn)
首先使用pandas讀取兩個(gè)excel,分別df1和df2,然后使用pandas自帶的合并函數(shù)實(shí)現(xiàn)匯總遇八,自動(dòng)對(duì)齊矛绘、處理缺失值。
pandas讀取excel
import pandas as pd
#定義待讀取的文件名
filename1 = '省監(jiān)控-核心網(wǎng)-全量告警_38AB1AE7.xlsx'
filename2 = '省監(jiān)控-核心網(wǎng)-全量告警_964C9DCF.xlsx'
#使用pandas的函數(shù)讀取excel刃永,當(dāng)前目錄,直接寫文件名即可羊精,可以有很多參數(shù)斯够,這里指定所需sheet
df1 = pd.read_excel(filename1,sheetname='廠家-設(shè)備類型')
df2 = pd.read_excel(filename2,sheetname='廠家-設(shè)備類型')
以上代碼可以讀取兩個(gè)excel文件⌒酰可以通過pandas.head() 可以顯示前幾行數(shù)據(jù)读规,
可以快速查看DataFrame的格式,如列名燃少,數(shù)據(jù)格式等束亏,判斷是否正確載入數(shù)據(jù)。
代碼的關(guān)鍵是掌握pandas.read_excel()函數(shù)阵具。
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就是待讀取的文件路徑碍遍,可以是相對(duì)路徑或絕對(duì)路徑定铜。sheetname指定sheet,還有很多參數(shù)可以個(gè)性化讀取文件怕敬,這里無需使用揣炕。
同時(shí)pandas還提供了大量其他read類函數(shù),可以讀取其他類型文件东跪,如sql畸陡、csv等等。
pandas按照特定列合并處理
那么這兩個(gè)df如何按照廠家和設(shè)備類型對(duì)告警量進(jìn)行累加呢虽填,也就是按照“廠家”和“設(shè)備類型”兩列進(jìn)行處理丁恭。
有一個(gè)最直觀的辦法就是循環(huán),通過遍歷所有廠家和設(shè)備類型斋日,然后講兩個(gè)df對(duì)應(yīng)的值求和,涩惑,雖然想法很簡(jiǎn)單,但是實(shí)現(xiàn)還是很復(fù)雜桑驱。
首先的知道兩個(gè)文件的最大廠家竭恬、設(shè)備類型集合,然后再遍歷熬的。
#獲取兩個(gè)文件的最大廠家痊硕、設(shè)備類型集合
cjlx1 = [tuple(df1.loc[i][['廠家','設(shè)備類型']]) for i in df1.index]
cjlx2 = [tuple(df2.loc[i][['廠家','設(shè)備類型']]) for i in df2.index]
cjlx = set(cjlx1+cjlx2)
#遍歷最大設(shè)備類型集合,求得合并告警量
rows = [] #輸出列表集合押框,可以轉(zhuǎn)換為DataFrame
for i in cjlx: #遍歷最大(廠家岔绸、類型)集合
cj = i[0] #獲取廠家名稱
lx = i[1] #獲取設(shè)備類型
if i in cjlx1: #如果該(廠家,類型)對(duì)在文件1中橡伞,取得對(duì)應(yīng)告警量,否則告警量為0
num1 = df1[(df1['廠家'] == cj) & (df1['設(shè)備類型']==lx)]['告警量'].iloc[0]
else:
num1 = 0
if i in cjlx2:
num2 = df2[(df2['廠家'] == cj) & (df2['設(shè)備類型']==lx)]['告警量'].iloc[0]
else:
num2 = 0
num = num1 + num2 #兩個(gè)告警量相加
row = [cj,lx,num] #生成新的一行數(shù)據(jù)
rows.append(row) #追加到輸出列表
data = pd.DataFrame(rows,columns = ['廠家','設(shè)備類型','告警量']) #轉(zhuǎn)換為DataFrame
可以看到最新的data已經(jīng)是匯總后的數(shù)據(jù)盒揉,一個(gè)excel有71行數(shù)據(jù),一個(gè)78行數(shù)據(jù)兑徘,合并后數(shù)據(jù)80行刚盈。
In[112]: len(df1),len(df2),len(data) #In表示輸入,冒號(hào)后才是真實(shí)代碼
Out[112]: (71, 78, 80)
In[113]:data.head()
Out[113]:
廠家 設(shè)備類型 告警量
0 CISCO 交換機(jī) 2021
1 東信 HOST 56
2 東信 交換機(jī) 16
3 中興 HOST 182
4 愛立信 HSS_SLF 2
上面代碼涉及了Python的常用語法和Pandas的基本操作挂脑,可以簡(jiǎn)單分享下藕漱,更多內(nèi)容需要自己查閱。
- python list的基本操作
創(chuàng)建[1,2,3]崭闲,append肋联,list1+list2,列表表達(dá)式創(chuàng)建新list [ i for i in x] 可以對(duì)i進(jìn)行操作變換刁俭,可以加條件過濾橄仍,如
In[114]:[i*2 for i in range(5) if i >2] #In表示輸入,冒號(hào)后才是真實(shí)代碼
Out[114]: [6, 8]
In[115]:[i*2 for i in range(5) ]
Out[115]: [0, 2, 4, 6, 8]
還有Python的集合set、元組tuple的基礎(chǔ)操作侮繁,建議自學(xué)虑粥。
- for if基本控制流
通過for循環(huán)遍歷,用in關(guān)鍵字實(shí)現(xiàn)遍歷鼎天,if...else實(shí)現(xiàn)條件判斷舀奶,最簡(jiǎn)單示例如下:
for i in range(5):
print(i)
if i>2:
print('我是if為真的結(jié)果:',i*2)
0
1
2
3
我是if為真的結(jié)果: 6
4
我是if為真的結(jié)果: 8
- Pandas的基本操作
DataFrame可以遍歷索引index,可以快速選取某些值斋射。
.loc 可以獲取某個(gè)位置值育勺,df[bool] 可以快速選擇bool為真的那些行數(shù)據(jù)。
如何快速選取某些行或某些列罗岖。如
In[118]:df1.loc[0][['廠家','設(shè)備類型']]
Out[118]:
廠家 CISCO
設(shè)備類型 HOST
Name: 0, dtype: object
In[119]:df1.loc[0]['廠家']
Out[119]: 'CISCO'
In[121]: bl = df1['廠家'] == cj
In[122]: len(bl),bl[0] #得到長(zhǎng)度為71的bool值list
Out[122]: (71, False)
In[123]:df1[bl] #獲取bl為真的那些行數(shù)據(jù)
Out[123]:
廠家 設(shè)備類型 告警量
7 愛立信 BSC 254984
8 愛立信 CG 175
9 愛立信 HLR_FE 4100
更多內(nèi)容關(guān)注Pandas官網(wǎng)或者其他相關(guān)教程涧至。
Pandas優(yōu)化處理
Pandas是一個(gè)非常優(yōu)秀的數(shù)據(jù)處理庫,實(shí)現(xiàn)上述功能肯定不用這么復(fù)雜桑包,有一些自帶的函數(shù)可以快速合并南蓬、規(guī)整兩個(gè)DataFrame。主要有append哑了、merge和concat等操作赘方。
- append
可以在df后添加行或者另一個(gè)df,有一些限制弱左。然后對(duì)df3進(jìn)行分組groupby窄陡,對(duì)告警量列進(jìn)行求和即可(分組建會(huì)作為index,需要提取出來作為新的一列):
In[125]:df3 = df1.append(df2)
In[125]:len(df1),len(df2),len(df3) #df3的行數(shù)是df1 和df2的和
Out[125]: (71, 78, 149)
- merge
通過一個(gè)或多個(gè)鍵(列名)將行連接起來拆火。多種連接方式跳夭,左連接,右連接等们镜。
其中on表示用哪些鍵連接起來币叹,how表示連接方式。
In[135]:df4 = pd.merge(df1,df2,on=['廠家','設(shè)備類型'],how = 'outer')
In[136]:df1.shape,df2.shape,df4.shape, #可以發(fā)現(xiàn)df4是80*4,其中80行已經(jīng)是最大集合模狭,是我們想要的結(jié)果颈抚,
Out[136]: ((71, 3), (78, 3), (80, 4))
In[137]:df4.head()
Out[137]:
廠家 設(shè)備類型 告警量_x 告警量_y
0 CISCO HOST 16.0 1.0
1 CISCO 交換機(jī) 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是原先兩個(gè)告警量,重名會(huì)自動(dòng)增加_x和_y胞皱,以便區(qū)分邪意。
接下來只需將這兩列相加即可。
df4['告警量'] = df4['告警量_x'] + df4['告警量_y']
這樣直接相加會(huì)有問題反砌,存在nan值問題。
因此我們需要將nan值替換為0萌朱,再求和宴树。修改后代碼如下
#使用pd.merge() 快速連接
data = pd.merge(df1,df2,on=['廠家','設(shè)備類型'],how = 'outer') #連接兩個(gè)df
data = data.fillna(0) #用0替換nan值
data['告警量'] = data['告警量_x'] + data['告警量_y'] #兩個(gè)告警量相加,得到新的一列告警量
data = data[['廠家','設(shè)備類型','告警量']] #只選取我們想要的三列
以上生成的data和一開始循環(huán)遍歷結(jié)果一樣晶疼,但是代碼非常精簡(jiǎn)酒贬。同樣concat可以實(shí)現(xiàn)類似的連接又憨,然后再進(jìn)行處理。請(qǐng)自行實(shí)現(xiàn)锭吨。
Pandas文件保存
Pandas可以非常方便將文件保存為各種格式蠢莺,如df.to_csv()、df.to_excel()零如。建議直接使用to_csv躏将,簡(jiǎn)單快速。
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
可以看到本地目錄已經(jīng)有out.csv考蕾。其中encoding設(shè)置了編碼方式祸憋、index可以設(shè)置是否保存索引。還有更多參數(shù)關(guān)注官網(wǎng)及其他教程肖卧。
代碼優(yōu)化
上面已經(jīng)實(shí)現(xiàn)了核心功能蚯窥,下面將代碼優(yōu)化一下,以便更好用塞帐。
優(yōu)化1 基本功能函數(shù)化
編寫一個(gè)函數(shù)拦赠,輸入兩個(gè)df,返回求和后df葵姥。
def get_df_sums(df1,df2):
if df1.empty: #檢查其中一個(gè)df為空
return df2
elif df2.empty:
return df1
else:
data = pd.merge(df1,df2,on=['廠家','設(shè)備類型'],how = 'outer') #連接兩個(gè)df
data = data.fillna(0) #用0替換nan值
data['告警量'] = data['告警量_x'] + data['告警量_y'] #兩個(gè)告警量相加荷鼠,得到新的一列告警量
data = data[['廠家','設(shè)備類型','告警量']] #只選取我們想要的三列
return data
優(yōu)化2 自動(dòng)讀取多個(gè)文件
一般情況下,待匯總的文件不止兩個(gè)牌里,我們可以使用Python腳本自動(dòng)讀取某個(gè)特定路徑下所有文件颊咬。
import os
path = r"C:\Users\zhuf0\Documents\repository\pythondemo"
out = pd.DataFrame()
for filename in os.listdir(path):#遍歷指定路徑的所有文件名
if '省監(jiān)控-核心網(wǎng)-全量告警' in filename: #選擇指定文件待讀取
filename = path+"\\"+filename #獲取絕對(duì)路徑
df1 = pd.read_excel(filename,sheetname='廠家-設(shè)備類型') #讀取該sheet
out = get_df_sums(out,df1) #和之前的out累積求和,類似 sum=sum+i
只需要將待匯總的文件放到指定目錄即可牡辽,輸出out.csv喳篇。
優(yōu)化3 排序后保存
Pandas 有很強(qiáng)大的排序功能,可以按照多列排序态辛。sort_values
如按照告警量排序:
In[164]out = out.sort_values(by = '告警量',ascending = False) #按照告警量降序排列
In[164]:out.head()
Out[164]:
廠家 設(shè)備類型 告警量
7 愛立信 BSC 481307.0
65 中興 MME 163725.0
14 愛立信 MME 99553.0
13 愛立信 MGW 37488.0
16 愛立信 MSC_Server 24683.0
留個(gè)問題麸澜,能否分組排序,按廠家分組奏黑,如愛立信炊邦,然后組內(nèi)告警量降序排列。廠家的排序方式按照該廠家的最大告警量排序熟史,而不是廠家的名稱馁害。如愛立信后是中興。
最終代碼
為了更像一個(gè)腳本蹂匹,也為了后續(xù)擴(kuò)展復(fù)用碘菜,最終優(yōu)化后腳本為,保存為pandas_demo.py文件:
import pandas as pd
import os
#給定兩個(gè)df,返回求和后結(jié)果
def get_df_sums(df1,df2):
if df1.empty: #檢查其中一個(gè)df為空
return df2
elif df2.empty:
return df1
else:
data = pd.merge(df1,df2,on=['廠家','設(shè)備類型'],how = 'outer') #連接兩個(gè)df
data = data.fillna(0) #用0替換nan值
data['告警量'] = data['告警量_x'] + data['告警量_y'] #兩個(gè)告警量相加,得到新的一列告警量
data = data[['廠家','設(shè)備類型','告警量']] #只選取我們想要的三列
return data
#給定路徑path忍啸,求和指定格式全部文件
def get_all_path(path):
out = pd.DataFrame()
for filename in os.listdir(path):#遍歷指定路徑的所有文件名
if '省監(jiān)控-核心網(wǎng)-全量告警' in filename: #選擇指定文件待讀取
filename = path+"\\"+filename #獲取絕對(duì)路徑
df1 = pd.read_excel(filename,sheetname='廠家-設(shè)備類型') #讀取該sheet
out = get_df_sums(out,df1) #和之前的out累積求和仰坦,類似 sum=sum+i
out = out.sort_values(by = '告警量',ascending = False)
return out
#作為主程序運(yùn)行
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) #保存
腳本使用
只要我們將待處理的文件放到該目錄,然后命令行運(yùn)行該腳本即可计雌。
執(zhí)行 python pandas_demo.py 沒有任何提示悄晃,說明成功。
打開out.csv查看
同樣我們可以進(jìn)一步優(yōu)化將路徑作為參數(shù)提供凿滤,指定輸出目錄妈橄,增加一些提示性輸出,增加腳本穩(wěn)健性鸭巴。
Python數(shù)據(jù)分析功能非常強(qiáng)大眷细,網(wǎng)上也有很多很好的項(xiàng)目和教程,大家可以結(jié)合實(shí)際工作問題鹃祖,選擇合適項(xiàng)目入門學(xué)習(xí)溪椎。
時(shí)間優(yōu)先,沒有太多排版恬口。如有錯(cuò)誤請(qǐng)及時(shí)反饋校读。
下一篇我們學(xué)習(xí)用Python自動(dòng)運(yùn)維——簡(jiǎn)明Python開發(fā)教程(4):網(wǎng)絡(luò)自動(dòng)化運(yùn)維的曙光