2017-07-20 61 views
0

我需要使用python按給定行排序excel電子表格。對於測試中,我使用這個數據(文件名爲xlwings sorting.xlsx):使用xlwings排序(pywin32)

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文檔或有關列排序的pywin32文檔中,似乎沒有任何文檔(如果有什麼,它埋得太深,以至於兩天的閱讀沒有發現它)。

我可以在網上找到的最接近的東西是this question,它沒有答案,只是重定向到一個沒有解析的github bug線程。

不過,我設法湊齊根據提問的下面的代碼:

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) 

這將運行,但我不知道語法是如何工作的。我什至不知道爲什麼第一個range('a2')電話是必要的,但它會拋出一個例外,如果我嘗試直接撥打sht.api.Sort。我試着直接看ipython的代碼?功能,但它只是給我<xlwings._xlwindows.COMRetryObjectWrapper object at 0x0000001375A459E8>沒有文檔字符串。然後,我嘗試通過.py文件中的ctrl + F實現Sort()函數,但在大量的COM包裝器中遇到了死衚衕,並且無法追蹤包含函數的實際模塊。無論如何,即使我不知道如何,測試案例的工作原理;所以下一步就是將這個函數放入一個包含excel工作簿和工作表的類中,以將該函數用作方法。我重寫既可以作爲一種方法和採取的字符串,而不是列數的代碼(新列添加到工作表的中間頻繁,所以數量會經常變化):

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 

有誰知道這東西的語法的工作原理或方法裏面放的時候,爲什麼它打破?

回答

0

原來這是一個令人難以置信的微妙的錯誤,所以我想我會發布答案,以防有人在一年中試圖做類似的事情。

總之,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