python實現Excel檔案讀取的程式(附原始碼)
python實現Excel檔案讀取的程式
前一段時間幫一個朋友用python寫了一個讀Excel程式操作的程式,具體要求為:讀取兩個Excel檔案,根據其中某個特徵的特徵值對這兩個檔案進行取交集操作,生成三個Excel檔案,第一個Excel檔案為這兩個檔案的公共集,第二個Excel檔案為第一個Excel檔案除去公共項後的Excel檔案,第三個Excel檔案為第二個Excel檔案除去公共項後的Excel檔案。
最近在做一個銷售價格預測的專案,資料量也是百萬級別,在做這個專案的過程中又重新認識了一下python中大資料處理神器pandas模組,發現pandas對於表格類資料的處理效率非常高(以前對於pandas的理解只是停留在是一個模組包,和array、list、directory型別類似只是多了一些方法,所以不以為然,現在發現他對於表格處理內含的函式真的很多和很強大,對於大量資料的處理效率真的很高,特別適合資料探勘領域中的特徵處理),於是乎想著將這個Excel檔案處理程式用pandas模組進行改進,結果效果和預期一樣,之前用xlrd和xlsxwriter模組寫的程式拿一個數據量30w+和一個數據量50w+的兩張表格做測試耗時兩個小時多(後文有介紹),現在換成pandas模組時間僅用不到五分鐘(不可思議),主要耗時在與檔案讀取和寫入,主要處理思想如下:
(1)將兩表內要比較的列匯出來並轉換為set型別
(2)對兩個set集合取交集,然後轉換為list型別
(3)用pandas內建的方法將兩表交集內的行找出來,並匯入一張新表內。
(4)依次求出兩表和其交集的差集,並匯入到新表內。
總結:對於大量資料的處理,pandas就是一款神器,其功能強大,效率比自己寫的for迴圈要高很多。
程式實現如下
打包成單個可執行檔案命令:installer -F MainInterface.py -p ExcelDealFunc.py --hidden-import ExcelDealFunc.py
*********分割線 以下是改進前採用xlrd和xlsxwriter模組對Excel檔案進行處理,這裡僅供參考*********# -*- coding: utf-8 -*- import pandas as pd import time def exceldealfunc(filename1,filename2,filename3,filename4,filename5,str1,str2): """ 函式名:exceldealfunc(filename1,filename2,filename3,filename4,filename5,num1,num2) 函式功能:執行表1表2取公共部分,並生成公共集表3,表1的去除公共集的表4,表2的去除公共集的表5 輸入 1: filename1:讀取表1的檔案路徑 輸入 1: filename2:讀取表2的檔案路徑 輸入 1: filename3:寫入表3的檔案路徑 輸入 1: filename4:寫入表4的檔案路徑 輸入 1: filename5:寫入表5的檔案路徑 輸入 2: str1:表1要比較的列號名 輸入 2: str2:表2要比較的列號名 輸出 1: 無 其他說明:無 """ start=time.time() table1=pd.read_excel(filename1) # 讀取表1 print("Time:%d s,Read Table1 Successful!"%(time.time()-start)) table2=pd.read_excel(filename2) # 讀取表2 print("Time:%d s,Read Table2 Successful!"%(time.time()-start)) print() set1=set(table1[str1]) # 將表1要比較的列轉換為集合格式(集合1) print("Time:%d s,File1 turn into a set Successful!"%(time.time()-start)) set2=set(table2[str2]) # 將表2要比較的列轉換為集合格式(集合2) print("Time:%d s,File2 turn into a set Successful!"%(time.time()-start)) print() set3=set1 & set2 # 取集合1和集合2的交集set3 print("Time:%d s,set3 = set1 & set2 Successful!"%(time.time()-start)) list3=list(set3) # 將set3轉換為列表的格式 print("Time:%d s,set3 turn to a list Successful!"%(time.time()-start)) table1[table1[str1].isin(list3)].to_excel(filename3,index=False,) # 將交集儲存到表3 print("Time:%d s,Write to Table3 Successful!"%(time.time()-start)) print() list4=list(set1 - set3) # 取集合1和交集的差集,並轉換為列表格式 print("Time:%d s,(set1 - set3) turn to a list Successful!"%(time.time()-start)) table1[table1[str1].isin(list4)].to_excel(filename4,index=False,) # 將差集1儲存到表4 print("Time:%d s,Write to Table4 Successful!"%(time.time()-start)) print() list5=list(set2 - set3) # 取集合2和交集的差集,並轉換為列表格式 print("Time:%d s,(set2 - set3) turn to a list Successful!"%(time.time()-start)) table2[table2[str2].isin(list5)].to_excel(filename5,index=False,) # 將差集2儲存到表5 print("Time:%d s,Write to Table5 Successful!"%(time.time()-start)) print() print("All task finish! Using Time:%d s."%(time.time()-start))
由於朋友所需要處理的資料量比較大,給我的是一個30w的資料集和一個50w的資料集小樣本,這裡記錄一下我碰到的兩個坑:
(1)xlwt模組儲存的檔案格式是xls,最多隻能夠寫入65535行列256列,如果行數或列數過多就會報錯。解決方法有兩種,第一種是繼續使用該模組,每65535行後或者256列後就新建一張sheet,將超過的資料儲存到新sheet中;第二種方法是使用xlsxwriter模組,xlsxwriter模組儲存的檔案格式是xlsx,能夠最多寫入1048576行65535列,如果資料量還是過大隻能用在該基礎上再用第一種方法了。筆者用的是第二種。
(2)python列表中對列表直接操作速度會明顯快於對列表索引操作,所以變成實現時儘量多用python內建列表操作(如a in list或者list=list1&list2等等),筆者一開始為了變成方便在其中用了列表索引操作,結果從晚上6點半執行到晚上11半都沒出結果,第二天早上來實驗室才發現執行完畢,將程式碼中對列表索引操作改成了對list操作後再次執行,結果只運行了兩個小時。
程式介面說明:(後文附原始碼與GitHub連結)
這款軟體只要選好兩個Excel檔案,以及對應要比較的列號(從0開始計數),然後設定好要存放生成的Excel檔案地址與檔名然後直接點選開始就可以了。
重置按鈕只是在執行完一次後啟用開始按鈕的作用。
筆者電腦配置如下:
測試了資料量分別為50w+和30w+的兩張表,用時是2個小時左右。
下面直接pose出原始碼:
GitHub下載地址:MyGitHub連結
檔案1:GUI視窗
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter.filedialog import askdirectory
from tkinter.filedialog import askopenfilename
from ExcelDealFunc import exceldealfunc
import os
class MyMainFace(object):
"""主介面類"""
def __init__(self):
"""
函式名:__init__(self)
函式功能:MyMainFace類的建構函式,介面元件均在此構造出的
輸入 1: MyMainFace類的物件,自身,無需輸入
輸出 1: 無
其他說明:無
"""
self.root = Tk()
self.root.title('Excel處理程式')
# 請選擇目標路徑
Label(self.root,text = "請選擇目標路徑:",fg="red").grid(row = 0, column = 0,sticky="w")
Label(self.root,text = "表1目標路徑:").grid(row = 1, column = 0,sticky="e")
self.path1 = StringVar()
Entry(self.root, width=60,textvariable = self.path1,state='readonly').grid(row = 1, column = 1,columnspan=3)
Button(self.root, text = "路徑選擇", command = self.selectPath1).grid(row = 1, column = 4)
self.num1 = StringVar()
Label(self.root,text = " 比較欄位的ID").grid(row = 1, column = 5,sticky="e")
Entry(self.root, width=5,textvariable = self.num1).grid(row = 1, column = 6)
Label(self.root,text = "表2目標路徑:").grid(row = 2, column = 0,sticky="e")
self.path2 = StringVar()
Entry(self.root,width=60,textvariable = self.path2,state='readonly').grid(row = 2, column = 1,columnspan=3)
Button(self.root, text = "路徑選擇", command = self.selectPath2).grid(row = 2, column = 4)
self.num2 = StringVar()
Label(self.root,text = " 比較欄位的ID").grid(row = 2, column = 5,sticky="e")
Entry(self.root, width=5,textvariable = self.num2).grid(row = 2, column = 6)
# 請選擇生成表格路徑
Label(self.root,text = "請選擇生成表格路徑:",fg="red").grid(row = 4, column = 0,sticky="w")
Label(self.root,text = "表格存放路徑:").grid(row = 5, column = 0,sticky="e")
self.path3 = StringVar()
Entry(self.root,width=60,textvariable = self.path3,state='readonly').grid(row = 5, column = 1,columnspan=3)
Button(self.root, text = "路徑選擇", command = self.selectPath3).grid(row = 5, column = 4)
# 請輸入生成表格名稱
Label(self.root,text = "請輸入生成表格名稱:",fg="red").grid(row = 6, column = 0,sticky="w")
Label(self.root,text = "表1表2的交集表的表名:").grid(row = 7, column = 0,sticky="e")
self.name1 = StringVar()
Entry(self.root, textvariable = self.name1).grid(row = 7, column = 1,sticky="w")
Label(self.root,text = "表1去除交集後的表名:").grid(row = 8, column = 0,sticky="e")
self.name2 = StringVar()
Entry(self.root, textvariable = self.name2).grid(row = 8, column = 1,sticky="w")
Label(self.root,text = "表2去除交集後的表名:").grid(row = 9, column = 0,sticky="e")
self.name3 = StringVar()
Entry(self.root, textvariable = self.name3).grid(row = 9, column = 1,sticky="w")
self.labeltxt=StringVar()
self.labeltxt.set(" ")
Label(self.root,textvariable = self.labeltxt,fg="red").grid(row = 7, column = 3)
Label(self.root,text = "以表1為參考",fg="red").grid(row = 7, column = 2)
self.var = StringVar()
self.var.set("開始")
self.button = Button(self.root,textvariable = self.var,command = self.start, width = 5)
self.button.grid(row = 8,column = 2,padx = 5)
self.var2 = StringVar()
self.var2.set("重置")
self.button2 = Button(self.root,textvariable = self.var2,command = self.reset, width = 5)
self.button2.grid(row = 9,column = 3,padx = 5)
# 建立一個背景色為白色的矩形
self.canvas = Canvas(self.root,width = 170,height = 26,bg = "white")
# 建立一個矩形外邊框(距離左邊,距離頂部,矩形寬度,矩形高度),線型寬度,顏色
self.out_line = self.canvas.create_rectangle(2,2,180,27,width = 1,outline = "black")
self.canvas.grid(row = 8,column = 3,ipadx = 5)
self.root.mainloop()
def start(self):
"""
函式名:start(self)
函式功能:開始按鈕的功能函式
輸入 1: MyMainFace類的物件,自身
輸出 1: 無
其他說明:無
"""
if self.path1.get():
filename1=self.path1.get()
else:
self.labeltxt.set("請選擇好表1")
return
if self.path2.get():
filename2=self.path2.get()
else:
self.labeltxt.set("請選擇好表2")
return
if self.path3.get():
filename=self.path3.get()
else:
filename="data"
if self.name1.get():
filename3=filename+"/"+self.name1.get()+".xlsx"
else:
filename3=filename+"/Table1_Table2.xlsx"
if self.name2.get():
filename4=filename+"/"+self.name2.get()+".xlsx"
else:
filename4=filename+"/Table1_del.xlsx"
if self.name3.get():
filename5=filename+"/"+self.name3.get()+".xlsx"
else:
filename5=filename+"/Table2_del.xlsx"
if self.num1.get():
num1=int(self.num1.get())
else:
self.labeltxt.set("請選擇好num1")
return
if self.num2.get():
num2=int(self.num2.get())
else:
self.labeltxt.set("請選擇好num2")
return
self.button.config(state="disable") # 關閉按鈕1功能
self.root.withdraw()
os.system('cls')
print("正在執行中請稍等...")
exceldealfunc(filename1,filename2,filename3,filename4,filename5,num1,num2)
self.root.deiconify()
self.labeltxt.set("Finally")
def scheduleshow(self,i):
fill_line = self.canvas.create_rectangle(2,2,0,27,width = 0,fill = "blue")
self.canvas.coords(fill_line, (0, 0, 180*i, 30))
self.var.set(str(round(100*i,1))+"%")
self.root.update()
def reset(self):
"""
函式名:reset(self)
函式功能:重置按鈕的功能函式
輸入 1: MyMainFace類的物件,自身
輸出 1: 無
其他說明:無
"""
self.button.config(state="active") # 啟用按鈕1
fill_line = self.canvas.create_rectangle(2,2,0,27,width = 0,fill = "white")
self.var.set("開始")
self.labeltxt.set(" ")
self.canvas.coords(fill_line, (0, 0, 181, 30))
self.root.update()
def selectPath1(self):
"""
函式名:selectPath1(self)
函式功能:選擇路徑1按鈕的功能函式
輸入 1: MyMainFace類的物件,自身
輸出 1: 無
其他說明:無
"""
path_ = askopenfilename(filetypes = [('Excel', '*.xls*')])
self.path1.set(path_)
def selectPath2(self):
"""
函式名:selectPath2(self)
函式功能:選擇路徑2按鈕的功能函式
輸入 1: MyMainFace類的物件,自身
輸出 1: 無
其他說明:無
"""
path_ = askopenfilename(filetypes = [('Excel', '*.xls*')])
self.path2.set(path_)
def selectPath3(self):
"""
函式名:selectPath3(self)
函式功能:選擇路徑3按鈕的功能函式
輸入 1: MyMainFace類的物件,自身
輸出 1: 無
其他說明:無
"""
path_ = askdirectory()
self.path3.set(path_)
##############################程式入口#########################################
if __name__=="__main__":
MyMainFace()
檔案2:Excel檔案處理模組
# -*- coding: utf-8 -*-
import xlrd,xlsxwriter
import sys
def writeoneline(table_from,table_to,line,nrows):
"""
函式名:writeoneline(table_from,table_to,line,nrows)
函式功能:讀取table_from的第line行資料寫入到table_to的第nrows行
輸入1: table_from:從該表內讀取要寫入的資料
輸入2: table_to:向該表內新增一行資料
輸入3: line:table_from內要讀取資料的行數
輸入4: nrows:table_to內要寫入資料的行數
輸出1: 無
其他說明:無
"""
write_data=table_from.row_values(line)
for i in range(len(write_data)):
table_to.write(nrows,i,write_data[i])
def readdata(filename,num):
"""
函式名:readdata(filename,num)
函式功能:通過檔案路徑,開啟Excel檔案,讀取sheet
輸入 1: filename:讀取表的檔案路徑
輸入 2: num:要比較的列號
輸出 1: table:sheet表資訊
輸出 2: nrows:該Excel表的行數
輸出 3: count:count要比較列的資料列表
其他說明:無
"""
data = xlrd.open_workbook(filename)
table = data.sheets()[0]
nrows = table.nrows
count=table.col_values(num)
return table,nrows,count
def exceldealfunc(filename1,filename2,filename3,filename4,filename5,num1,num2):
"""
函式名:exceldealfunc(filename1,filename2,filename3,filename4,filename5,num1,num2)
函式功能:執行表1表2取公共部分,並生成公共集表3,表1的去除公共集的表4,表2的去除公共集的表5
輸入 1: filename1:讀取表1的檔案路徑
輸入 1: filename2:讀取表2的檔案路徑
輸入 1: filename3:寫入表3的檔案路徑
輸入 1: filename4:寫入表4的檔案路徑
輸入 1: filename5:寫入表5的檔案路徑
輸入 2: num1:表1要比較的列號
輸入 2: num2:表2要比較的列號
輸出 1: 無
其他說明:無
"""
table1,nrows1,count1=readdata(filename1,num1)
print("Read Table1 Successful!")
table2,nrows2,count2=readdata(filename2,num2)
print("Read Table2 Successful!")
data3 = xlsxwriter.Workbook(filename3) # 共同使用者樣本
table3 = data3.add_worksheet()
data4 = xlsxwriter.Workbook(filename4) # Table1處理後的樣本Table4
table4 = data4.add_worksheet()
data5 = xlsxwriter.Workbook(filename5) # Table2處理後的樣本Table5
table5 = data5.add_worksheet()
writeoneline(table2,table3,0,0)
writeoneline(table1,table4,0,0)
writeoneline(table2,table5,0,0)
nrows3=1
nrows4=1
nrows5=1
for i in range(1,nrows1):
if count1[i] in count2:
writeoneline(table1,table3,i,nrows3) # Table1寫入共同使用者樣本Table3
nrows3+=1
else:
writeoneline(table1,table4,i,nrows4) # Table1寫入處理後的樣本Table4
nrows4+=1
if i%10000==0:
done=i/(nrows1+nrows2)
sys.stdout.write("\r[%s%s] %d%%" % ('█'*int(40*done),' '*(40-int(40*done)),int(100*done)))
sys.stdout.flush()
data3.close()
print("Write to Table3 Successful!")
data4.close()
print("Write to Table4 Successful!")
done=nrows2/(nrows1+nrows2)
sys.stdout.write("\r[%s%s] %d%%" % ('█'*int(40*done),' '*(40-int(40*done)),int(100*done)))
sys.stdout.flush()
for i in range(1,nrows2):
if count2[i] not in count1:
writeoneline(table2,table5,i,nrows5) # table2寫入處理後的樣本Table5
nrows5+=1
if i%10000==0:
done=(i+nrows2)/(nrows1+nrows2)
sys.stdout.write("\r[%s%s] %d%%" % ('█'*int(40*done),' '*(40-int(40*done)),int(100*done)))
sys.stdout.flush()
data5.close()
print("Write to Table5 Successful!")