将 Excel 文件读取到 pandas 数据帧的更快方法

Faster way to read Excel files to pandas dataframe

我有一个 14MB Excel file with five worksheets 正在读入 Pandas 数据框,尽管下面的代码有效,但需要 9 分钟!

有没有人有加快速度的建议?

import pandas as pd

def OTT_read(xl,site_name):
    df = pd.read_excel(xl.io,site_name,skiprows=2,parse_dates=0,index_col=0,
                       usecols=[0,1,2],header=None,
                       names=['date_time','%s_depth'%site_name,'%s_temp'%site_name])
    return df

def make_OTT_df(FILEDIR,OTT_FILE):
    xl = pd.ExcelFile(FILEDIR + OTT_FILE)
    site_names = xl.sheet_names
    df_list = [OTT_read(xl,site_name) for site_name in site_names]
    return site_names,df_list

FILEDIR='c:/downloads/'
OTT_FILE='OTT_Data_All_stations.xlsx'
site_names_OTT,df_list_OTT = make_OTT_df(FILEDIR,OTT_FILE)

正如其他人所建议的那样,csv 读取速度更快。因此,如果您在 windows 上并拥有 Excel,您可以调用 vbscript 将 Excel 转换为 csv,然后读取 csv。我尝试了下面的脚本,它花了大约 30 秒。

# create a list with sheet numbers you want to process
sheets = map(str,range(1,6))

# convert each sheet to csv and then read it using read_csv
df={}
from subprocess import call
excel='C:\Users\rsignell\OTT_Data_All_stations.xlsx'
for sheet in sheets:
    csv = 'C:\Users\rsignell\test' + sheet + '.csv' 
    call(['cscript.exe', 'C:\Users\rsignell\ExcelToCsv.vbs', excel, csv, sheet])
    df[sheet]=pd.read_csv(csv)

这里有一小段 python 用于创建 ExcelToCsv.vbs 脚本:

#write vbscript to file
vbscript="""if WScript.Arguments.Count < 3 Then
    WScript.Echo "Please specify the source and the destination files. Usage: ExcelToCsv <xls/xlsx source file> <csv destination file> <worksheet number (starts at 1)>"
    Wscript.Quit
End If

csv_format = 6

Set objFSO = CreateObject("Scripting.FileSystemObject")

src_file = objFSO.GetAbsolutePathName(Wscript.Arguments.Item(0))
dest_file = objFSO.GetAbsolutePathName(WScript.Arguments.Item(1))
worksheet_number = CInt(WScript.Arguments.Item(2))

Dim oExcel
Set oExcel = CreateObject("Excel.Application")

Dim oBook
Set oBook = oExcel.Workbooks.Open(src_file)
oBook.Worksheets(worksheet_number).Activate

oBook.SaveAs dest_file, csv_format

oBook.Close False
oExcel.Quit
""";

f = open('ExcelToCsv.vbs','w')
f.write(vbscript.encode('utf-8'))
f.close()

本回答受益于Convert XLS to CSV on command line and csv & xlsx files import to pandas data frame: speed issue

如果少于 65536 行(在每个 sheet 中),您可以尝试 xls(而不是 xlsx。根据我的经验,xlsxlsx。很难和csv比较,因为它取决于sheet的个数。

虽然这不是一个理想的解决方案(xls 是一种二进制旧的私有格式),但我发现如果您正在使用 很多,这将很有用sheets、内部公式具有经常更新的值,或出于任何原因你真的很想保持excel多sheet功能(而不是csv分隔文件)。

我知道这是旧的,但以防万一其他人正在寻找不涉及 VB 的答案。 Pandas read_csv() 更快,但您不需要 VB 脚本来获取 csv 文件。

打开您的 Excel 文件并另存为 *.csv(逗号分隔值)格式。

在工具下,您可以 select Web 选项,在编码选项卡下,您可以将编码更改为适合您的数据的编码。我最终使用了 Windows,西欧,因为 Windows UTF 编码是 "special",但是有很多方法可以完成同样的事情。然后使用 pd.read_csv() 中的编码参数来指定您的编码。

编码选项已列出here

没有理由打开excel 如果您愿意处理一次缓慢的转换。

  1. 使用 pd.read_excel()
  2. 将数据读入数据帧
  3. 立即使用 pd.to_csv()
  4. 将其转储到 csv 中

避免 excel 和 windows 特定调用。就我而言,一次性点击是值得的。我有一个☕.

根据我的经验,Pandas read_excel() 可以很好地处理包含多个 sheet 的 Excel 文件。正如 Using Pandas to read multiple worksheets 中所建议的那样,如果您将 sheet_name 分配给 None,它会自动将每个 sheet 放入一个 Dataframe 中,并且它会输出一个 Dataframes 字典,其键为 sheet 名字。

但需要时间的原因在于您在代码中解析文本 的位置。 14MB excel 和 5 sheets 并不算多。我有一个 20.1MB excel 的文件,其中包含 46 个 sheets,每个文件超过 6000 行和 17 列,使用 read_excel 如下所示:

t0 = time.time()

def parse(datestr):
    y,m,d = datestr.split("/")
    return dt.date(int(y),int(m),int(d))

data = pd.read_excel("DATA (1).xlsx", sheet_name=None, encoding="utf-8", skiprows=1, header=0, parse_dates=[1], date_parser=parse)

t1 = time.time()

print(t1 - t0)
## result: 37.54169297218323 seconds

在上面的代码中,data 是一个包含 46 个数据帧的字典。

正如其他人所建议的那样,使用 read_csv() 会有所帮助,因为读取 .csv 文件会更快。但考虑到 .xlsx 文件使用压缩这一事实,.csv 文件可能更大,因此读取速度更慢。但是,如果您想使用 python 将文件转换为逗号分隔(VBcode 由 Rich Signel), you can use: Convert xlsx to csv

提供

我使用 xlsx2csv 在内存中将 excel 文件虚拟转换为 csv,这有助于将读取时间缩短一半左右。

from xlsx2csv import Xlsx2csv
from io import StringIO
import pandas as pd


def read_excel(path: str, sheet_name: str) -> pd.DataFrame:
    buffer = StringIO()
    Xlsx2csv(path, outputencoding="utf-8", sheet_name=sheet_name).convert(buffer)
    buffer.seek(0)
    df = pd.read_csv(buffer)
    return df

我鼓励您自己进行比较,看看哪种方法适合您的情况。

例如,如果您正在处理大量 XLSX 文件并且每个文件只读一次,您可能不想担心 CSV 转换。但是,如果您打算一遍又一遍地阅读 CSV,那么我强烈建议将工作簿中的每个工作表保存一次到 csv,然后使用 pd.read_csv().

重复阅读它们

下面是一个简单的脚本,可以让您比较 Importing XLSX DirectlyConverting XLSX to CSV in memoryImporting CSV。它基于 答案。

剧透警告:如果您要多次读取文件,将 XLSX 转换为 CSV 会更快。

我对我正在处理的一些文件进行了一些测试,这是我的结果:

5,874 KB xlsx file (29,415 rows, 58 columns)
Elapsed time for [Import XLSX with Pandas]:    0:00:31.75
Elapsed time for [Convert XLSX to CSV in mem]: 0:00:22.19
Elapsed time for [Import CSV file]:            0:00:00.21

********************
202,782 KB xlsx file (990,832 rows, 58 columns)
Elapsed time for [Import XLSX with Pandas]:    0:17:04.31
Elapsed time for [Convert XLSX to CSV in mem]: 0:12:11.74
Elapsed time for [Import CSV file]:            0:00:07.11

是的!与 XLSX 的 17 分钟相比,202MB 的文件确实只用了 7 秒!!!

如果您准备好设置自己的测试,只需在 Excel 中打开 XLSX 并将其中一张工作表保存为 CSV。对于最终解决方案,您显然需要遍历工作表来处理每个工作表。

您还需要pip install rich pandas xlsx2csv

from rich import print
import pandas as pd
from datetime import datetime
from xlsx2csv import Xlsx2csv
from io import StringIO

def timer(name, startTime = None):
    if startTime:
        print(f"Timer: Elapsed time for [{name}]: {datetime.now() - startTime}")
    else:
        startTime = datetime.now()
        print(f"Timer: Starting [{name}] at {startTime}")
        return startTime


def read_excel(path: str, sheet_name: str) -> pd.DataFrame:
    buffer = StringIO()
    Xlsx2csv(path, outputencoding="utf-8", sheet_name=sheet_name).convert(buffer)
    buffer.seek(0)
    df = pd.read_csv(buffer)
    return df


xlsxFileName = "MyBig.xlsx"
sheetName = "Sheet1"
csvFileName = "MyBig.csv"

startTime = timer(name="Import XLSX with Pandas")
df = pd.read_excel(xlsxFileName, sheet_name=sheetName)
timer("Import XLSX with Pandas", startTime)

startTime = timer(name="Convert XLSX to CSV first")
df = read_excel(path=xlsxFileName, sheet_name=sheetName)
timer("Convert XLSX to CSV first", startTime)

startTime = timer(name="Import CSV")
df = pd.read_csv(csvFileName)
timer("Import CSV", startTime)