使用 xlwings 排序 (pywin32)
Sorting with xlwings (pywin32)
我需要使用 python 按给定行对 excel 分布 sheet 进行排序。对于测试,我正在使用此数据(在名为 xlwings sorting.xlsx 的文件中):
Numbers Letters Letters_2
7 A L
6 B K
5 C M
4 D J
3 E N
2 F I
1 G H
应该归类为:
Numbers Letters Letters_2
1 G H
2 F I
3 E N
4 D J
5 C M
6 B K
7 A L
人们会认为这是一项微不足道的任务,但似乎没有任何文档(如果有的话,它被埋得太深了,两天的阅读都没有发现它) xlwings docs or the pywin32 个关于列排序的
我在网上能找到的最接近的东西是 this question,它没有答案,只是重定向到一个没有解决方案的 github 错误线程。
不过,我还是设法根据提问者的代码拼凑了以下代码:
import xlwings as xw
from xlwings.constants import SortOrder
bk = xw.Book(r'C:\Users\username\Documents\Test Files\xlwings sorting.xlsx')
sht = bk.sheets['Sheet1']
def xl_col_sort(sht,col_num):
sht.range('a2').api.Sort(sht.range((2,col_num)).api,SortOrder.xlAscending)
return
xl_col_sort(sht,1)
这 运行s,但我不知道语法是如何工作的。我什至不知道为什么第一个 range('a2')
调用是必要的,但如果我尝试直接调用 sht.api.Sort
,它会抛出异常。我试着用 ipython 的 ?? 直接查看代码功能,但它只给我 <xlwings._xlwindows.COMRetryObjectWrapper object at 0x0000001375A459E8>
没有文档字符串。然后我尝试实际 ctrl+F 通过 Sort()
函数的 .py 文件,但是 运行 进入了一个巨大的 COM 包装器列表的死胡同,并且无法追踪包含的实际模块函数。
无论如何,即使我不知道测试用例是如何工作的;所以下一步是将此函数放入包含 excel 工作簿和 sheet 的 class 中以将该函数用作方法。我重写了代码,既用作方法又采用字符串而不是列号(新列经常添加到工作中间sheet,因此数字会经常更改):
class Metrics:
# self.sheet is a sheet object based on self.book opened with xlwings
# a bunch of other methods and attributes
def xl_col_sort(self,col):
# +2 because excel starts at 1 (+1) and the dataframe self.df
# uses a data column as the index (+1)
col_num = np.where(self.df.columns == col)[0][0] + 2
so = xw.constants.SortOrder
self.sheet.range('a2').api.Sort(self.sheet.range((2,col_num)).api, so.xlAscending)
return
我看不出这里有任何功能上的变化。它仍然收到相同的参数,即使它们经过一个额外的步骤来创建。然而尝试 运行 这会产生 MemoryError
:
In[1]: metrics.xl_col_sort('Exp. Date')
---------------------------------------------------------------------------
MemoryError Traceback (most recent call last)
<ipython-input-3-f1de8b0e8e98> in <module>()
----> 1 metrics.xl_col_sort('Exp. Date')
C:\Users\username\Documents\Projects\PyBev\pyBev_0-3-1\pybev\metricsobj.py in xl_col_sort(self, col)
146 so = xw.constants.SortOrder
147
--> 148 self.sheet.range('a2').api.Sort(self.sheet.range((2,col_num)).api, so.xlAscending)
149 return
150 # def monday_backup(self):
C:\Users\username\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\xlwings\main.py in range(self, cell1, cell2)
818 raise ValueError("Second range is not on this sheet")
819 cell2 = cell2.impl
--> 820 return Range(impl=self.impl.range(cell1, cell2))
821
822 @property
C:\Users\username\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\xlwings\_xlwindows.py in range(self, arg1, arg2)
576 if 0 in arg1:
577 raise IndexError("Attempted to access 0-based Range. xlwings/Excel Ranges are 1-based.")
--> 578 xl1 = self.xl.Cells(arg1[0], arg1[1])
579 elif isinstance(arg1, numbers.Number) and isinstance(arg2, numbers.Number):
580 xl1 = self.xl.Cells(arg1, arg2)
C:\Users\username\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\xlwings\_xlwindows.py in __call__(self, *args, **kwargs)
149 for i in range(N_COM_ATTEMPTS + 1):
150 try:
--> 151 v = self._inner(*args, **kwargs)
152 t = type(v)
153 if t is CDispatch:
C:\Users\username\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\win32com\client\dynamic.py in __call__(self, *args)
190 if invkind is not None:
191 allArgs = (dispid,LCID,invkind,1) + args
--> 192 return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
193 raise TypeError("This dispatch object does not define a default method")
194
MemoryError: CreatingSafeArray
有谁知道这个东西的语法是如何工作的,或者为什么它在放入方法时会中断?
事实证明这是一个非常微妙的错误,所以我想我会 post 答案,以防有人在一年内用谷歌搜索这个来尝试做类似的事情。
简而言之,sheet.range()
方法只接受整数坐标,表达式:
col_num = np.where(self.df.columns == col)[0][0] + 2
产生一个浮点数。为什么这会产生 MemoryError
而不是语法错误,这超出了我的理解范围,但可能是一个疏忽。 The devs do seem to know about it, though.
此外,上述文档中没有列出语法,因为它实际上是 VBA 代码,如发现 here. Sort()
方法仅适用于 Range
对象,因此第一个 sht.range()
调用要求。
最后,如果有人想要一个简化的函数来封装所有这些废话:
import xlwings as xw
bk = xw.Book(file_path)
sheet = bk.sheets['Sheet1'] # or whatever the sheet is named
def xl_col_sort(sheet,col_num):
sheet.range((2,col_num)).api.Sort(Key1=sheet.range((2,col_num)).api, Order1=1)
return
我需要使用 python 按给定行对 excel 分布 sheet 进行排序。对于测试,我正在使用此数据(在名为 xlwings sorting.xlsx 的文件中):
Numbers Letters Letters_2
7 A L
6 B K
5 C M
4 D J
3 E N
2 F I
1 G H
应该归类为:
Numbers Letters Letters_2
1 G H
2 F I
3 E N
4 D J
5 C M
6 B K
7 A L
人们会认为这是一项微不足道的任务,但似乎没有任何文档(如果有的话,它被埋得太深了,两天的阅读都没有发现它) xlwings docs or the pywin32 个关于列排序的
我在网上能找到的最接近的东西是 this question,它没有答案,只是重定向到一个没有解决方案的 github 错误线程。
不过,我还是设法根据提问者的代码拼凑了以下代码:
import xlwings as xw
from xlwings.constants import SortOrder
bk = xw.Book(r'C:\Users\username\Documents\Test Files\xlwings sorting.xlsx')
sht = bk.sheets['Sheet1']
def xl_col_sort(sht,col_num):
sht.range('a2').api.Sort(sht.range((2,col_num)).api,SortOrder.xlAscending)
return
xl_col_sort(sht,1)
这 运行s,但我不知道语法是如何工作的。我什至不知道为什么第一个 range('a2')
调用是必要的,但如果我尝试直接调用 sht.api.Sort
,它会抛出异常。我试着用 ipython 的 ?? 直接查看代码功能,但它只给我 <xlwings._xlwindows.COMRetryObjectWrapper object at 0x0000001375A459E8>
没有文档字符串。然后我尝试实际 ctrl+F 通过 Sort()
函数的 .py 文件,但是 运行 进入了一个巨大的 COM 包装器列表的死胡同,并且无法追踪包含的实际模块函数。
无论如何,即使我不知道测试用例是如何工作的;所以下一步是将此函数放入包含 excel 工作簿和 sheet 的 class 中以将该函数用作方法。我重写了代码,既用作方法又采用字符串而不是列号(新列经常添加到工作中间sheet,因此数字会经常更改):
class Metrics:
# self.sheet is a sheet object based on self.book opened with xlwings
# a bunch of other methods and attributes
def xl_col_sort(self,col):
# +2 because excel starts at 1 (+1) and the dataframe self.df
# uses a data column as the index (+1)
col_num = np.where(self.df.columns == col)[0][0] + 2
so = xw.constants.SortOrder
self.sheet.range('a2').api.Sort(self.sheet.range((2,col_num)).api, so.xlAscending)
return
我看不出这里有任何功能上的变化。它仍然收到相同的参数,即使它们经过一个额外的步骤来创建。然而尝试 运行 这会产生 MemoryError
:
In[1]: metrics.xl_col_sort('Exp. Date')
---------------------------------------------------------------------------
MemoryError Traceback (most recent call last)
<ipython-input-3-f1de8b0e8e98> in <module>()
----> 1 metrics.xl_col_sort('Exp. Date')
C:\Users\username\Documents\Projects\PyBev\pyBev_0-3-1\pybev\metricsobj.py in xl_col_sort(self, col)
146 so = xw.constants.SortOrder
147
--> 148 self.sheet.range('a2').api.Sort(self.sheet.range((2,col_num)).api, so.xlAscending)
149 return
150 # def monday_backup(self):
C:\Users\username\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\xlwings\main.py in range(self, cell1, cell2)
818 raise ValueError("Second range is not on this sheet")
819 cell2 = cell2.impl
--> 820 return Range(impl=self.impl.range(cell1, cell2))
821
822 @property
C:\Users\username\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\xlwings\_xlwindows.py in range(self, arg1, arg2)
576 if 0 in arg1:
577 raise IndexError("Attempted to access 0-based Range. xlwings/Excel Ranges are 1-based.")
--> 578 xl1 = self.xl.Cells(arg1[0], arg1[1])
579 elif isinstance(arg1, numbers.Number) and isinstance(arg2, numbers.Number):
580 xl1 = self.xl.Cells(arg1, arg2)
C:\Users\username\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\xlwings\_xlwindows.py in __call__(self, *args, **kwargs)
149 for i in range(N_COM_ATTEMPTS + 1):
150 try:
--> 151 v = self._inner(*args, **kwargs)
152 t = type(v)
153 if t is CDispatch:
C:\Users\username\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\win32com\client\dynamic.py in __call__(self, *args)
190 if invkind is not None:
191 allArgs = (dispid,LCID,invkind,1) + args
--> 192 return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
193 raise TypeError("This dispatch object does not define a default method")
194
MemoryError: CreatingSafeArray
有谁知道这个东西的语法是如何工作的,或者为什么它在放入方法时会中断?
事实证明这是一个非常微妙的错误,所以我想我会 post 答案,以防有人在一年内用谷歌搜索这个来尝试做类似的事情。
简而言之,sheet.range()
方法只接受整数坐标,表达式:
col_num = np.where(self.df.columns == col)[0][0] + 2
产生一个浮点数。为什么这会产生 MemoryError
而不是语法错误,这超出了我的理解范围,但可能是一个疏忽。 The devs do seem to know about it, though.
此外,上述文档中没有列出语法,因为它实际上是 VBA 代码,如发现 here. Sort()
方法仅适用于 Range
对象,因此第一个 sht.range()
调用要求。
最后,如果有人想要一个简化的函数来封装所有这些废话:
import xlwings as xw
bk = xw.Book(file_path)
sheet = bk.sheets['Sheet1'] # or whatever the sheet is named
def xl_col_sort(sheet,col_num):
sheet.range((2,col_num)).api.Sort(Key1=sheet.range((2,col_num)).api, Order1=1)
return