利用pypy加速pyxlsbwriter生成xlsb文件

发布于:2025-08-18 ⋅ 阅读:(20) ⋅ 点赞:(0)

上文介绍了python通过DuckDB和pyxlsbwriter模块生成xlsb文件,因为python是解释执行,它的速度有点慢,pypy是另一种python解释器,它使用即时编译(JIT)技术来提高执行速度。
因为DuckDB与pypy不兼容,所以让DeepSeek帮助编写了不依赖DuckDB和pandas,直接使用python csv模块将csv文件内容写入多sheet的xlsb文件的程序。
源代码csv2xlsb.py如下:

import csv
import math
from pyxlsbwriter import XlsbWriter

def csv_to_xlsb(
    csv_file,
    output_file,
    sheet_prefix="Sheet",
    max_rows_per_sheet=1048575,  # XLSB 单 Sheet 最大行数
    compression_level=6,
    has_header=True
):
    """
    将大型 CSV 文件分 Sheet 写入 XLSB 文件
    
    参数:
        csv_file (str): 输入 CSV 文件路径
        output_file (str): 输出 XLSB 文件路径
        sheet_prefix (str): Sheet 名称前缀
        max_rows_per_sheet (int): 每个 Sheet 最大行数
        compression_level (int): 压缩级别(0-9)
        has_header (bool): CSV 是否包含标题行
    """
    # 首先计算总行数(为了确定需要的 Sheet 数量)
    with open(csv_file, 'r', newline='', encoding='utf-8') as f:
        total_rows = sum(1 for _ in csv.reader(f)) - (1 if has_header else 0)
    
    num_sheets = math.ceil(total_rows / max_rows_per_sheet)
    print(f"CSV 文件共有 {total_rows} 行数据,将分成 {num_sheets} 个 Sheet 写入")

    with XlsbWriter(output_file, compressionLevel=compression_level) as writer:
        with open(csv_file, 'r', newline='', encoding='utf-8') as f:
            csv_reader = csv.reader(f)
            
            # 读取标题行(如果有)
            headers = next(csv_reader) if has_header else None
            
            current_sheet = 0
            current_row = 0
            sheet_data = []
            
            # 添加标题行到第一个 Sheet
            if headers:
                sheet_data.append(headers)
            
            for row in csv_reader:
                sheet_data.append(row)
                current_row += 1
                
                # 当达到最大行数时写入当前 Sheet 并创建新 Sheet
                if current_row >= max_rows_per_sheet:
                    # 写入当前 Sheet
                    sheet_name = f"{sheet_prefix}_{current_sheet + 1}"
                    writer.add_sheet(sheet_name)
                    writer.write_sheet(sheet_data)
                    print(f"已写入 Sheet: {sheet_name},行数: {len(sheet_data)}")
                    
                    # 准备下一个 Sheet
                    current_sheet += 1
                    current_row = 0
                    sheet_data = []
                    if headers:  # 新 Sheet 也包含标题行
                        sheet_data.append(headers)
            
            # 写入最后一个 Sheet(可能未达到最大行数)
            if sheet_data:
                sheet_name = f"{sheet_prefix}_{current_sheet + 1}"
                writer.add_sheet(sheet_name)
                writer.write_sheet(sheet_data)
                print(f"已写入 Sheet: {sheet_name},行数: {len(sheet_data)}")

if __name__ == "__main__":
    # 示例用法
    csv_to_xlsb(
        csv_file="5m Sales Records.csv",
        output_file="sheets.xlsb",
        sheet_prefix="Data",
        max_rows_per_sheet=1048575,  # 每个 Sheet 100 万行
        compression_level=6,
        has_header=True
    )

下面是在pypy中安装pyxlsbwriter并执行的步骤

C:\d\pypy>pypy3 -m ensurepip
Successfully installed pip-24.0 setuptools-65.5.0

C:\d\pypy>pypy3 -m pip install pyxlsbwriter
Successfully installed pyxlsbwriter-0.0.3

C:\d\pypy>cd ..

C:\d>timer64 pypy\pypy3 csv2xlsb.py

Kernel  Time =     1.750 =    2%
User    Time =    58.546 =   97%
Process Time =    60.296 =  100%    Virtual  Memory =   3858 MB
Global  Time =    60.271 =  100%    Physical Memory =   3828 MB

我把第一步生成的sheets.xlsb文件改名为pypysheets.xlsb,以便比较。再用python执行同样的代码。

C:\d>move sheets.xlsb pypysheets.xlsb
移动了         1 个文件。

C:\d>timer64 python csv2xlsb.py
CSV 文件共有 5000000 行数据,将分成 5 个 Sheet 写入
已写入 Sheet: Data_1,行数: 1048576
已写入 Sheet: Data_2,行数: 1048576
已写入 Sheet: Data_3,行数: 1048576
已写入 Sheet: Data_4,行数: 1048576
已写入 Sheet: Data_5,行数: 805701


Kernel  Time =     1.640 =    1%
User    Time =    85.218 =   97%
Process Time =    86.859 =   99%    Virtual  Memory =   4728 MB
Global  Time =    87.267 =  100%    Physical Memory =   4724 MB

可见pypy3执行时间比python缩短了三分之一。再比较两个生成的文件,大小完全一致,再用FC命令比较,发现还是存在差异的。

C:\d>dir *sheets.xlsb

2025/08/17  18:32       201,303,180 pypysheets.xlsb
2025/08/17  18:34       201,303,180 sheets.xlsb

C:\d>fc /b sheets.xlsb pypysheets.xlsb
正在比较文件 sheets.xlsb 和 PYPYSHEETS.XLSB
024E15D8: 37 F5
024E15D9: 94 93
024E16D0: 37 F5
024E16D1: 94 93
...
0BFFA380: 57 0A
0BFFA3BD: 57 0A
0BFFA3F8: 57 0A

那pypy生成的文件到底对不对,能不能用,还是用rust_sheet插件来读取,比较如下:
先读取用python生成的sheets.xlsb

D create table xlsb as from read_sheet('sheets.xlsb',header=1,sheet_name='Data_1')
union all from read_sheet('sheets.xlsb',header=1,sheet_name='Data_2')
union all from read_sheet('sheets.xlsb',header=1,sheet_name='Data_3')
union all from read_sheet('sheets.xlsb',header=1,sheet_name='Data_4')
union all from read_sheet('sheets.xlsb',header=1,sheet_name='Data_5');
100% ▕████████████████████████████████████████████████████████████▏
Run Time (s): real 47.218 user 52.890625 sys 2.437500
D summarize xlsb;
┌────────────────┬─────────────┬─────────────┬────────────────────┬───┬───────┬───────┬─────────┬─────────────────┐
│  column_name   │ column_type │     min     │        max         │ … │  q50  │  q75  │  count  │ null_percentage │
│    varcharvarcharvarcharvarchar       │   │ int32 │ int32 │  int64  │  decimal(9,2)   │
├────────────────┼─────────────┼─────────────┼────────────────────┼───┼───────┼───────┼─────────┼─────────────────┤
│ Region         │ VARCHAR     │ Asia        │ Sub-Saharan Africa │ … │  NULLNULL50000000.00 │
│ Country        │ VARCHAR     │ Afghanistan │ Zimbabwe           │ … │  NULLNULL50000000.00 │
│ Item TypeVARCHAR     │ Baby Food   │ Vegetables         │ … │  NULLNULL50000000.00 │
│ Sales Channel  │ VARCHAR     │ Offline     │ Online             │ … │  NULLNULL50000000.00 │
│ Order Priority │ VARCHAR     │ C           │ M                  │ … │  NULLNULL50000000.00 │
│ Order DateVARCHAR1/1/20109/9/2020           │ … │  NULLNULL50000000.00 │
│ Order ID       │ VARCHAR100000321999999892          │ … │  NULLNULL50000000.00 │
│ Ship DateVARCHAR1/1/20109/9/2020           │ … │  NULLNULL50000000.00 │
│ Units Sold     │ VARCHAR19999               │ … │  NULLNULL50000000.00 │
│ Unit Price     │ VARCHAR109.289.33               │ … │  NULLNULL50000000.00 │
│ Unit Cost      │ VARCHAR117.1197.44              │ … │  NULLNULL50000000.00 │
│ Total Revenue  │ VARCHAR1000003.46999931.76          │ … │  NULLNULL50000000.00 │
│ Total Cost     │ VARCHAR1000.23999979.98          │ … │  NULLNULL50000000.00 │
│ Total Profit   │ VARCHAR100.2499997.92           │ … │  NULLNULL50000000.00 │
├────────────────┴─────────────┴─────────────┴────────────────────┴───┴───────┴───────┴─────────┴─────────────────┤
│ 14 rows                                                                                    12 columns (8 shown) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Run Time (s): real 0.238 user 2.031250 sys 0.140625

再读取用pypy生成, 改名后的pypysheets.xlsb

D create table xlsb as from read_sheet('pypysheets.xlsb',header=1,sheet_name='Data_1')
union all from read_sheet('pypysheets.xlsb',header=1,sheet_name='Data_2')
union all from read_sheet('pypysheets.xlsb',header=1,sheet_name='Data_3')
union all from read_sheet('pypysheets.xlsb',header=1,sheet_name='Data_4')
union all from read_sheet('pypysheets.xlsb',header=1,sheet_name='Data_5');
100% ▕████████████████████████████████████████████████████████████▏
Run Time (s): real 45.734 user 43.078125 sys 2.531250
D summarize xlsb;
┌────────────────┬─────────────┬─────────────┬────────────────────┬───┬───────┬───────┬─────────┬─────────────────┐
│  column_name   │ column_type │     min     │        max         │ … │  q50  │  q75  │  count  │ null_percentage │
│    varcharvarcharvarcharvarchar       │   │ int32 │ int32 │  int64  │  decimal(9,2)   │
├────────────────┼─────────────┼─────────────┼────────────────────┼───┼───────┼───────┼─────────┼─────────────────┤
│ Region         │ VARCHAR     │ Asia        │ Sub-Saharan Africa │ … │  NULLNULL50000000.00 │
│ Country        │ VARCHAR     │ Afghanistan │ Zimbabwe           │ … │  NULLNULL50000000.00 │
│ Item TypeVARCHAR     │ Baby Food   │ Vegetables         │ … │  NULLNULL50000000.00 │
│ Sales Channel  │ VARCHAR     │ Offline     │ Online             │ … │  NULLNULL50000000.00 │
│ Order Priority │ VARCHAR     │ C           │ M                  │ … │  NULLNULL50000000.00 │
│ Order DateVARCHAR1/1/20109/9/2020           │ … │  NULLNULL50000000.00 │
│ Order ID       │ VARCHAR100000321999999892          │ … │  NULLNULL50000000.00 │
│ Ship DateVARCHAR1/1/20109/9/2020           │ … │  NULLNULL50000000.00 │
│ Units Sold     │ VARCHAR19999               │ … │  NULLNULL50000000.00 │
│ Unit Price     │ VARCHAR109.289.33               │ … │  NULLNULL50000000.00 │
│ Unit Cost      │ VARCHAR117.1197.44              │ … │  NULLNULL50000000.00 │
│ Total Revenue  │ VARCHAR1000003.46999931.76          │ … │  NULLNULL50000000.00 │
│ Total Cost     │ VARCHAR1000.23999979.98          │ … │  NULLNULL50000000.00 │
│ Total Profit   │ VARCHAR100.2499997.92           │ … │  NULLNULL50000000.00 │
├────────────────┴─────────────┴─────────────┴────────────────────┴───┴───────┴───────┴─────────┴─────────────────┤
│ 14 rows                                                                                    12 columns (8 shown) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Run Time (s): real 0.240 user 1.843750 sys 0.343750

读取时间基本一致,内容也一致。
所以,可以放心利用pypy加速pyxlsbwriter生成xlsb文件。


网站公告

今日签到

点亮在社区的每一天
去签到