python 用 xlwings 处理 Excel 中的重复数据
xlwings 簡介
xlwings 是一個 Python 庫。簡化了 Python 和 Excel 通信。
xlwings - 讓Excel跑得飛快!
本文寫作背景 & 需求 & 方案
因前幾個月幫在醫院工作的朋友現學現賣用VBA寫了段程序,處理2個excel文檔的數據到第3個Excel文檔上,有模板數據,有圖表,怕數據出錯,反復測試,折騰了有2天才弄出來。在摸索怎么用VBA開發的過程中發現,VBA開發太痛苦了。
前幾天我無意間看到了一篇文章的標題 叫做 插上翅膀,讓Excel飛起來——xlwings ,我被標題吸引住了,看到了有這么個東西。
更巧的是昨天朋友又讓我幫她處理個Excel問題,這次的需求非常簡單,為:
1. 只有一張只有3列的表
2. 如果表中存在兩行或多行數據,它們的第2列和第3列數據都相同,第1列數據是否相同不考慮
3. 那么就只保留任意一行數據即可。
即:刪除表中后兩列數據相同的多余的行
于是乎想到了xlwings這個東西,想試一試,看看怎么玩(我還不會python的HelloWorld)
效果圖如下:
解決方案
- 代碼見本文編碼部分,推薦方案代碼2,實際情況不推薦!(2023/01/08更新:現在也不推薦方案代碼2了,推薦方案代碼3)
- 推薦優先不編碼!使用 WPS 或 Excel 已有功能去實現,如下圖,點幾下就OK了。本文旨在站在程序猿的角度玩一把編碼實現,嘗試下python的味道。
環境準備
推薦使用 anaconda 解決環境問題,具體用法自行百度
Python3 安裝
注意:安裝時提供管理員權限,否則容易出錯。
庫安裝
pip install 庫名[=版本號]
-
xlwings
pip install xlwings
-
其他庫(看需安裝)
- NumPy
- NumPy Matplotlib
- pandas
Python 語法基礎
>>> def fib(n): >>> a, b = 0, 1 >>> while a < n: >>> print(a, end=' ') >>> a, b = b, a+b >>> print() >>> fib(1000) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987中文報錯
需要首行加入 #coding=utf-8 或 # -*- coding: UTF-8 -*-
內置函數
https://docs.python.org/zh-cn/3.7/library/functions.html
- type() 用于檢測一個數據的數據類型,非常有用。效果如:<class ‘list’> <class ‘xlwings.main.Range’>
- str() 轉為字符串
- int() 轉為int
- len() 獲取序列或集合對象長度
- max() 返回可迭代對象中最大值,等等其他數學函數
- round(num, n) 四舍五入保留n位小數的函數
- input("請輸入:") 等待接收用戶輸入數據并將接收數據返回
嚴格代碼對齊
因為沒有大括號,必須要嚴格代碼對齊,否則語義可能不同甚至報錯!
序列類型 — list, tuple, range
-
列表list 是可變序列
class list([iterable])?
使用一對方括號來表示空列表: []
使用方括號,其中的項以逗號分隔: [a], [a, b, c]
使用列表推導式: [x for x in iterable]
使用類型的構造器: list() 或 list(iterable) -
元組tuple是不可變序列,通常用于儲存異構數據的多項集
class tuple([iterable])
使用一對圓括號來表示空元組: ()
使用一個后綴的逗號來表示單元組: a, 或 (a,)
使用以逗號分隔的多個項: a, b, c or (a, b, c)
使用內置的 tuple(): tuple() 或 tuple(iterable)
tuple(‘abc’) 返回 (‘a’, ‘b’, ‘c’) 而 tuple( [1, 2, 3] ) 返回 (1, 2, 3)。 如果沒有給出參數,構造器將創建一個空元組 () -
范圍range 類型表示不可變的數字序列,通常用于在 for 循環中循環指定的次數。
class range(stop)
class range(start, stop[, step]) -
舉例
>>> 3,0,-1 (3, 0, -1) >>> (3,0,-1) # 同上,為元組,括號大多可省,不建議省 (3, 0, -1) >>> range(3,0,-1) # range ,表示開始為3,結束為1,步長為-1的整數范圍 range(3, 0, -1) >>> for i in range(3,0,-1): ... print(i) ... 3 2 1
集合/字典
-
集合可用多種方式來創建:
使用花括號內以逗號分隔元素的方式: {‘jack’, ‘sjoerd’}
使用集合推導式: {c for c in ‘abracadabra’ if c not in ‘abc’}
使用類型構造器: set(), set(‘foobar’), set([‘a’, ‘b’, ‘foo’]) -
字典可用多種方式來創建:
使用花括號內以逗號分隔 鍵: 值 對的方式: {‘jack’: 4098, ‘sjoerd’: 4127} or {4098: ‘jack’, 4127: ‘sjoerd’}
使用字典推導式: {}, {x: x ** 2 for x in range(10)}
使用類型構造器: dict(), dict([(‘foo’, 100), (‘bar’, 200)]), dict(foo=100, bar=200)
for 循環,和其他語言還真不一樣
語法格式只有 for-in 這一種格式
"for" target_list "in" expression_list ":" suite["else" ":" suite]`因此,如果想動態修改循環次數,可使用while替換
r = 3 for i in range(r):print(i)# 下面的修改對for循環沒有用,因為i的值會被下次循環覆蓋,i的值在range范圍內i-=1r-=2 # 輸出: # 0 # 1 # 2字符串拼接
加號用于字符串拼接時,需要注意非字符串需要str()函數處理才可以,如:"你好" + str(123) ;或 print("你好%s"%123) 或更簡單的print(a, b, c...) 或 format()方法拼接:
"你好{1}{0}{2}".format(1,2,3) # ‘你好213’ "你好{}{}{}".format(1,2,3) # ‘你好123’ 等
冒號
-
if / for 等語句中,分割代碼作用
>>> if x < 0: ... x = 0 ... print('Negative changed to zero') ... elif x == 0: ... print('Zero') ... elif x == 1: ... print('Single') ... else: ... print('More') -
列表(類似C語言數組,Python中沒有數組)引用中
>>> x [1, 3, 5, 8, 9] >>> x[1:3] # 索引范圍 [3, 5] >>> x[:] # 全部 [1, 3, 5, 8, 9]包含3個子列表的一維列表 X=array( [[1,2,3,4], [5,6,7,8], [9,10,11,12]] )
X[:, 0] 就是取矩陣X的所有行的第0列的元素,X[:,1] 就是取所有行的第1列的元素
X[:, m:n] 即取矩陣X的所有行中的的第m到n-1列數據,含左不含右 -
雙冒號
推薦:官方文檔在Range處的解釋(點擊查看)
a[x:y:z]
x表示切片起點,y表示切片終點,z表示步長,步長z默認為1;
如果z為正數,則默認x、y分別為列表的開始和結束索引,內容的公式為 a[i] = start + step*i 其中 i >= 0 且 r[i] < stop;
如果z為負數,表示倒序,內容的公式仍然為 a[i] = start + step*i,但限制條件改為 i >= 0 且 a[i] > z.;
如果 a[0] 不符合值的限制條件,則該 a 對象為空。 a 對象確實支持負索引(最后一個元素的索引是-1,倒數第二個元素索引為-2),但是會將其解讀為從正索引所確定的序列的末尾開始索引;
如果z為0,則報錯。如下:
>>> a = [1,3,5,8] >>> a[::] [1, 3, 5, 8] >>> a[1:3:] [3, 5] >>> a[::2] [1, 5] >>> a[::-1] [8, 5, 3, 1] >>> a[::-2] [8, 3] >>> a[1::-2] [3] >>> a[0:3:-1] [] >>> a[3:0:-1] [8, 5, 3] >>> a[3:0:-2] [8, 3] >>> a[1:3:0] Traceback (most recent call last):File "<stdin>", line 1, in <module> ValueError: slice step cannot be zerorange(10)[::2] 表示范圍 [0, 10) 步長為2的切片
>>> range(10)[::2] range(0, 10, 2) -
切片完全指南(語法篇)
-
[xx for xx in yy] 鏈表推導式
-
for 循環
遍歷列表的四種方法
類
https://www.runoob.com/python3/python3-class.html
class people:#定義基本屬性name = ''age = 0#定義私有屬性,私有屬性在類外部無法直接進行訪問__weight = 0#定義構造方法def __init__(self,n,a,w):self.name = nself.age = aself.__weight = wdef speak(self):print("%s 說: 我 %d 歲。" %(self.name,self.age))# 實例化類 p = people('runoob',10,30) p.speak()模塊和包
https://www.runoob.com/python3/python3-module.html
- 模塊,是一個python文件。包,是一個包含若干模塊的文件夾。
- 模塊導入方式
- import 模塊名 [as 別名]
- from 模塊名 import <成員名1 [as 別名1], 成員名2 [as 別名2],...> 或 from 包名 import *
- 使用方式
有別名的使用別名,導入成員的可直接使用成員,否則使用 模塊名.成員 - 包導入方式類似,包中必須包含文件__init__.py ,__all__
python 標準庫
python 標準庫
常用標準庫——菜鳥教程
- sys
print(sys.argv) # 程序執行參數
print(sys.platform) # 系統平臺
sys.exit()
sys.exit(“崩了”) - pprint
pprint.pprint(sys.modules) # 模塊字典
print(sys.path) # 模塊搜索路徑 - os
pprint.pprint(os.environ) # 格式化打印系統環境變量
print(os.environ[“path”]) # 打印path
os.system(“ls”) # 執行系統命令
print(os.getcwd()) # 當前的工作目錄 - 字符串正則匹配
- 數學
- 日期和時間
- 數據壓縮
- 訪問互聯網
零散知識點
- __name__ 屬性,主程序的該屬性值為 __main__
- dir() 函數,可以找到模塊內定義的所有名稱
編碼
本需求方案代碼 1 (不推薦)
該代碼雖然能得到正確的結果,但是 for i in range(rows-1): 語句內部雖然有對 rows 值的修改,但是i 在下次循環時又會被重新賦值,i 在下次循環的取值依然不會因循環體內部對 rows 的改變而改變。i 始終會將所有的 [0, rows-1) 內的整數值取完。可參見官方 for 語句解釋
#coding=utf-8 # 首行作用:防止中文亂碼 import xlwings as xw import operator# 打開Excel程序,默認設置為程序可見,只打開不新建 app = xw.App(visible=True,add_book=False) wb = xw.Book('test.xls') sht = wb.sheets[0]rng = sht.range('A1').expand('table') rows = rng.rows.count cols = rng.columns.count print(str(rows)+", "+ str(cols))# 將表中數據轉為二維數組格式 all = sht.range((1,1),(rows,cols)).valuefor i in range(rows-1):print("--"+str(i)+"---")print(range(rows-1,i,-1))for j in range(rows-1,i,-1):# print(str(i)+", "+str(j))# 先用strip()去除單元格數據中首尾空白,防止數據因空白影響結果if operator.eq(all[i][1].strip(),all[j][1].strip()) and operator.eq(all[i][2].strip(),all[j][2].strip()):# print(all[j])# print(str(j))rng.rows[j].api.EntireRow.Delete()del all[j]rows-=1wb.save() wb.close() app.quit()方案代碼2 (推薦)(2023/01/08更新:不再推薦這種自寫算法方案方式)
#coding=utf-8 import xlwings as xw# app = xw.App(visible=True, add_book=False) wb = xw.Book('ok.xls') # wb = app.books.open(r'D:/cuncaojin/desktop/ok.xls') # wb = xw.Book(r'D:/cuncaojin/desktop/ok.xls') sht = wb.sheets[0]# myList = [['2018年國家基本藥物采購目錄', '阿苯達唑', '片劑'], ['2018年國家基本藥物采購目錄', '阿法骨化醇', '片劑'], ['2018年國家 基本藥物采購目錄', '阿米卡星', '注射液'], ['2018年國家基本藥物采購目錄', '阿莫西林', '膠囊'], ['其他目錄', '阿莫西林', '顆粒劑'], ['其他目錄', '阿莫西林', '干混懸劑'], ['其他目錄', '阿莫西林', '分散片'], ['其他目錄', '阿莫西林', '膠囊'], ['2018年國家基本藥物采購目錄', '阿莫西林', '顆粒劑'], ['常用低價藥品基本藥物目錄', '阿莫西林', '分散片2'], ['2018年國家基 本藥物采購目錄', '阿莫西林', '膠囊'], ['常用低價藥品基本藥物目錄', '阿莫西林', '分散片'], ['2018年國家基本藥物采購目錄', '阿莫西林3', '膠囊'], ['2018年國家基本藥物采購目錄', '阿莫西林', '膠囊'], ['2018年國家基本藥物采購目錄', '阿莫西林', ' 膠囊']] myList = sht[0,0].current_region.value# print(len(myList),"\n", myList)i = 0 while i<len(myList)-1:for j in range(len(myList)-1,i,-1):if myList[i][1].strip()==myList[j][1].strip() and myList[i][2].strip()==myList[j][2].strip():# 移除重復數據myList.remove(myList[j])# 補償刪除造成的數據遷移i-=1i+=1sht['F1'].value = myList # sht['F1'].current_region.autofit() # sht['F1'].current_region.color = (221,170,244)# 打印最終數據行數 print("最終有效數據行數:%d"%len(myList))wb.save() wb.close() # 有時quit、甚至使用kill都無法關閉Excel # app.quit() # app.kill()# 退出Excel應用 for app in xw.apps:app.quit()方案代碼3 (推薦,更新于2023/01/08)
得助于CSDN工具里chatgpt 引擎力量,讓我初步了解了pandas庫這么好用。加上報錯自行百度,最終有此方案更新。
原表格數據
處理后表格數據
通過以上腳本,處理了帶有空白符數據的表格,日志如下:
目錄 藥品名 類型 0 2018年國家基本藥物采購目錄 阿苯達唑 片劑 1 2018年國家基本藥物采購目錄 阿法骨化醇 片劑 2 2018年國家 基本藥物采購目錄 阿米卡星 注射液 3 2018年國家基本藥物采購目錄 阿莫西林 膠囊 4 其他目錄 阿莫西林 顆粒劑 5 其他目錄 阿莫西林 干混懸劑 6 其他目錄 阿莫西林 分散片 7 其他目錄 阿莫西林 膠囊 8 2018年國家基本藥物采購目錄 阿莫西林 顆粒劑 9 常用低價藥品基本藥物目錄 阿莫西林 分散片2 10 2018年國家基 本藥物采購目錄 阿莫西林 膠囊 11 常用低價藥品基本藥物目錄 阿莫西林 分散片 12 2018年國家基本藥物采購目錄 阿莫西林3 膠囊 13 2018年國家基本藥物采購目錄 阿莫西林 膠囊 14 2018年國家基本藥物采購目錄 阿莫西林 膠囊 ///目錄 藥品名 類型 0 2018年國家基本藥物采購目錄 阿苯達唑 片劑 1 2018年國家基本藥物采購目錄 阿法骨化醇 片劑 2 2018年國家基本藥物采購目錄 阿米卡星 注射液 3 2018年國家基本藥物采購目錄 阿莫西林 膠囊 4 其他目錄 阿莫西林 顆粒劑 5 其他目錄 阿莫西林 干混懸劑 6 其他目錄 阿莫西林 分散片 7 其他目錄 阿莫西林 膠囊 8 2018年國家基本藥物采購目錄 阿莫西林 顆粒劑 9 常用低價藥品基本藥物目錄 阿莫西林 分散片2 10 2018年國家基本藥物采購目錄 阿莫西林 膠囊 11 常用低價藥品基本藥物目錄 阿莫西林 分散片 12 2018年國家基本藥物采購目錄 阿莫西林3 膠囊 13 2018年國家基本藥物采購目錄 阿莫西林 膠囊 14 2018年國家基本藥物采購目錄 阿莫西林 膠囊 ///目錄 藥品名 類型 0 2018年國家基本藥物采購目錄 阿苯達唑 片劑 1 2018年國家基本藥物采購目錄 阿法骨化醇 片劑 2 2018年國家基本藥物采購目錄 阿米卡星 注射液 3 2018年國家基本藥物采購目錄 阿莫西林 膠囊 4 其他目錄 阿莫西林 顆粒劑 9 常用低價藥品基本藥物目錄 阿莫西林 分散片2 12 2018年國家基本藥物采購目錄 阿莫西林3 膠囊吐槽
順便吐槽一下chatgpt,真是惡心透了,禁止咱們國家使用,明白地搞壟斷、搞封鎖。國內想使用該功能太麻煩了,要么搞國外服務器代理之類,要么掏錢使用國內中介服務,如使用CSDN的這個服務只免費使用不到5次,再想用就要辦VIP才能用。
給自己看的其它代碼
更多用法可訪問本文參考鏈接部分,強烈建議看Python 官方api 、xlwings 官方api 和 Excel 官方api 。
import matplotlib.pyplot as plt import xlwings as xwimport pandas as pd import numpy as npimport osexit = os.path.exists(r'E:\yg\desktop\test.xlsx')app=xw.App(visible=True,add_book=False)if(exit):wb = xw.Book(r'E:\yg\desktop\test.xlsx') else:wb=app.books.add()sht = wb.sheets[0]df = pd.DataFrame(np.random.rand(7, 4), columns=['aaa', 'bb', 'c', 'd']) ax = df.plot(kind='bar') fig = ax.get_figure() sht.pictures.add(fig, name='MyPlot', update=True)wb.save(r'E:\yg\desktop\test.xlsx') wb.close() app.quit()幫助
因沒接觸過python,也不懂VBA,因此本文內容可能存在若干不當或錯誤,如有發現,敬請斧正。
參考
總結
以上是生活随笔為你收集整理的python 用 xlwings 处理 Excel 中的重复数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于Kinect深度图像采集和SLAM室
- 下一篇: 利用python编写手机脚本