将 win32com.client 范围转换为 Pandas 数据框?
Convert win32com.client Range to Pandas Dataframe?
我正在编写一些调用 Python 代码的宏来对 Excel 中的范围执行操作。在 Python 中使用 pandas 可以更容易地完成许多必需的操作。因为我想在电子表格打开时执行此操作(并且可能尚未保存),所以我使用 win32com.client
读取一系列单元格以转换为 Pandas 数据框。但是,这个速度非常慢,大概是因为我的计算方式很低效吧:
import datetime
import pytz
import pandas
import time
import win32com.client
def range_to_table(excelRange, tsy, tsx, height, width, add_cell_refs = True):
ii = 0
keys = []
while ii < width:
keys.append(str(excelRange[ii]))
ii += 1
colnumbers = {key:jj+tsx for jj, key in enumerate(keys)}
keys.append('rownumber')
mydict = {key:[] for key in keys}
while ii < width*height:
mydict[keys[ii%width]].append(excelRange[ii].value)
ii += 1
for yy in range(tsy + 1, tsy + 1 + height - 1): # add 1 to not include header
mydict['rownumber'].append(yy)
return (mydict, colnumbers)
ExcelApp = win32com.client.GetActiveObject('Excel.Application')
wb = ExcelApp.Workbooks('myworkbook.xlsm')
sheet_num = [sheet.Name for sheet in wb.Sheets].index("myworksheet name") + 1
ws = wb.Worksheets(sheet_num)
height = int(ws.Cells(1, 3)) # obtain table height from formula in excel spreadsheet
width = int(ws.Cells(1, 2)) # obtain table width from formula in excel spreadsheet
myrange = ws.Range(ws.Cells(2, 1), ws.Cells(2 + height - 1, 1 + width - 1))
df, colnumbers = range_to_table(myrange, 1, 1, height, width)
df = pandas.DataFrame.from_dict(df)
这行得通,但是我编写的 range_to_table
函数对于大型 tables 来说非常慢,因为它逐个迭代每个单元格。
我怀疑可能有更好的方法将 Excel Range 对象转换为 Pandas 数据帧。你知道更好的方法吗?
这是我的范围的简化示例:
代码中的 height
和 width
变量取自 table:
正上方的单元格
这里有什么想法,还是我只需要保存工作簿并使用 Pandas 从保存的文件中读取 table?
您可以尝试为此使用多处理。例如,您可以让每个工作人员扫描不同的列,甚至可以在行上执行相同的操作。
需要对您的代码进行少量更改:
- 创建一个遍历列并将信息存储在字典中的函数
- 使用简单的多处理示例@https://pymotw.com/2/multiprocessing/basics.html
- 创建一个函数,将每个工人创建的所有不同字典附加到一个字典中
这应该将您的计算时间除以使用的工作器数量。
操作分为两部分:定义电子表格范围,然后将数据导入 Python。这是我正在使用的测试数据:
1.定义范围 :Excel 有一个称为 Dynamic Ranges 的特性。这允许您为范围可变的范围命名。
我设置了一个名为 'DynRange' 的动态范围,您可以看到它使用 $C$1 和 $C$2 中的行数和列数来定义数组的大小。
有了这个定义后,Python 中的 Name 就可以使用该范围,这样就不必显式访问行数和列数了。
2。通过 win32.com: 在 Python 中使用此范围 一旦您在 Excel 中定义了名称,在 Python 中处理它就容易多了。
import win32com.client as wc
import pandas as pd
#Create a dispatch interface
xl = wc.gencache.EnsureDispatch('Excel.Application')
filepath = 'SomeFilePath\TestBook.xlsx'
#Open the workbook
wb = xl.Workbooks.Open(filepath)
#Get the Worksheet by name
ws = wb.Sheets('Sheet1')
#Use the Value property to get all the data in the range
listVals = ws.Range('DynRange').Value
#Construct the dataframe, using first row as headers
df = pd.DataFrame(listVals[1:],columns=listVals[0])
#Optionally process the datetime value to avoid tz warnings
df['Datetime'] = df['Datetime'].dt.tz_convert(None)
print(df)
wb.Close()
输出:
Datetime Principal Source Amt Cost Basis
0 2021-04-21 04:59:00 -5.0 1.001 5.0
1 2021-04-25 15:16:00 -348.26 1.001 10.0
2 2021-04-29 11:04:00 0.0 1.001 5.0
3 2021-04-29 21:26:00 0.0 1.001 5.0
4 2021-04-29 23:39:00 0.0 1.001 5.0
5 2021-05-02 14:00:00 -2488.4 1.001 5.0
正如 OP 所怀疑的那样,逐个单元格地迭代范围执行缓慢。 COM 基础结构必须进行大量处理才能将数据从一个进程 (Excel) 传递到另一个进程 (Python)。这被称为 'marshalling'。大部分时间都花在了一侧打包变量和另一侧拆包上。一次性编组 Excel 范围的全部内容(作为二维数组)效率更高,Excel 通过在 Value
属性 上公开允许这样做范围作为一个整体,而不是按单元格。
我正在编写一些调用 Python 代码的宏来对 Excel 中的范围执行操作。在 Python 中使用 pandas 可以更容易地完成许多必需的操作。因为我想在电子表格打开时执行此操作(并且可能尚未保存),所以我使用 win32com.client
读取一系列单元格以转换为 Pandas 数据框。但是,这个速度非常慢,大概是因为我的计算方式很低效吧:
import datetime
import pytz
import pandas
import time
import win32com.client
def range_to_table(excelRange, tsy, tsx, height, width, add_cell_refs = True):
ii = 0
keys = []
while ii < width:
keys.append(str(excelRange[ii]))
ii += 1
colnumbers = {key:jj+tsx for jj, key in enumerate(keys)}
keys.append('rownumber')
mydict = {key:[] for key in keys}
while ii < width*height:
mydict[keys[ii%width]].append(excelRange[ii].value)
ii += 1
for yy in range(tsy + 1, tsy + 1 + height - 1): # add 1 to not include header
mydict['rownumber'].append(yy)
return (mydict, colnumbers)
ExcelApp = win32com.client.GetActiveObject('Excel.Application')
wb = ExcelApp.Workbooks('myworkbook.xlsm')
sheet_num = [sheet.Name for sheet in wb.Sheets].index("myworksheet name") + 1
ws = wb.Worksheets(sheet_num)
height = int(ws.Cells(1, 3)) # obtain table height from formula in excel spreadsheet
width = int(ws.Cells(1, 2)) # obtain table width from formula in excel spreadsheet
myrange = ws.Range(ws.Cells(2, 1), ws.Cells(2 + height - 1, 1 + width - 1))
df, colnumbers = range_to_table(myrange, 1, 1, height, width)
df = pandas.DataFrame.from_dict(df)
这行得通,但是我编写的 range_to_table
函数对于大型 tables 来说非常慢,因为它逐个迭代每个单元格。
我怀疑可能有更好的方法将 Excel Range 对象转换为 Pandas 数据帧。你知道更好的方法吗?
这是我的范围的简化示例:
代码中的 height
和 width
变量取自 table:
这里有什么想法,还是我只需要保存工作簿并使用 Pandas 从保存的文件中读取 table?
您可以尝试为此使用多处理。例如,您可以让每个工作人员扫描不同的列,甚至可以在行上执行相同的操作。
需要对您的代码进行少量更改:
- 创建一个遍历列并将信息存储在字典中的函数
- 使用简单的多处理示例@https://pymotw.com/2/multiprocessing/basics.html
- 创建一个函数,将每个工人创建的所有不同字典附加到一个字典中
这应该将您的计算时间除以使用的工作器数量。
操作分为两部分:定义电子表格范围,然后将数据导入 Python。这是我正在使用的测试数据:
1.定义范围 :Excel 有一个称为 Dynamic Ranges 的特性。这允许您为范围可变的范围命名。
我设置了一个名为 'DynRange' 的动态范围,您可以看到它使用 $C$1 和 $C$2 中的行数和列数来定义数组的大小。
有了这个定义后,Python 中的 Name 就可以使用该范围,这样就不必显式访问行数和列数了。
2。通过 win32.com: 在 Python 中使用此范围 一旦您在 Excel 中定义了名称,在 Python 中处理它就容易多了。
import win32com.client as wc
import pandas as pd
#Create a dispatch interface
xl = wc.gencache.EnsureDispatch('Excel.Application')
filepath = 'SomeFilePath\TestBook.xlsx'
#Open the workbook
wb = xl.Workbooks.Open(filepath)
#Get the Worksheet by name
ws = wb.Sheets('Sheet1')
#Use the Value property to get all the data in the range
listVals = ws.Range('DynRange').Value
#Construct the dataframe, using first row as headers
df = pd.DataFrame(listVals[1:],columns=listVals[0])
#Optionally process the datetime value to avoid tz warnings
df['Datetime'] = df['Datetime'].dt.tz_convert(None)
print(df)
wb.Close()
输出:
Datetime Principal Source Amt Cost Basis
0 2021-04-21 04:59:00 -5.0 1.001 5.0
1 2021-04-25 15:16:00 -348.26 1.001 10.0
2 2021-04-29 11:04:00 0.0 1.001 5.0
3 2021-04-29 21:26:00 0.0 1.001 5.0
4 2021-04-29 23:39:00 0.0 1.001 5.0
5 2021-05-02 14:00:00 -2488.4 1.001 5.0
正如 OP 所怀疑的那样,逐个单元格地迭代范围执行缓慢。 COM 基础结构必须进行大量处理才能将数据从一个进程 (Excel) 传递到另一个进程 (Python)。这被称为 'marshalling'。大部分时间都花在了一侧打包变量和另一侧拆包上。一次性编组 Excel 范围的全部内容(作为二维数组)效率更高,Excel 通过在 Value
属性 上公开允许这样做范围作为一个整体,而不是按单元格。