【问题标题】:Fastest Way To Run Through 50k Lines of Excel File in OpenPYXL在 OpenPYXL 中运行 50k 行 Excel 文件的最快方法
【发布时间】:2016-02-25 20:53:19
【问题描述】:

我在 python 中使用 openpyxl,我试图运行 50k 行并从每一行获取数据并将其放入文件中。但是..我发现它运行得越慢,我越深入它。前 1k 行的速度非常快,不到一分钟,但之后,完成接下来的 1k 行需要的时间越来越长。

我正在打开一个 .xlsx 文件。我想知道将 .txt 文件作为 csv 或其他文件打开或读取 json 文件或其他文件是否更快?或者以某种方式转换为阅读速度更快的东西?

我在给定列中有 20 个唯一值,然后每个值的值都是随机的。我正在尝试为每个值获取整个唯一值列的字符串。

值 1:1243,345,34,124, 值2:1243,345,34,124, 等等等等

我正在遍历值列表,查看文件中是否存在该名称,如果存在,那么它将访问该文件并将新值附加到该文件中,如果该文件不存在,它将创建该文件,然后将其设置为追加。我有一个字典,它连接了所有“附加写入文件”的东西,所以每当我想写东西时,它会获取文件名,附加的东西将在字典中可用,它会查找并写入该文件,因此它不会在每次运行时都打开新文件。

前 1k 只用了不到一分钟.. 现在我正在处理 4k 到 5k 的记录,并且它已经准备好运行 5 分钟.. 随着记录的增加,它似乎需要更长的时间,我想知道如何加快速度向上。它根本没有打印到控制台。

writeFile = 1
theDict = {}

for row in ws.iter_rows(rowRange):
    for cell in row:
        #grabbing the value
        theStringValueLocation = "B" + str(counter)
        theValue = ws[theStringValueLocation].value
        theName = cell.value
        textfilename = theName + ".txt"

        if os.path.isfile(textfilename):
            listToAddTo = theDict[theName]
            listToAddTo.write("," + theValue)
            if counter == 1000:
                print "1000"
                st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')

        else:
            writeFileName = open(textfilename, 'w')
            writeFileName.write(theValue)
            writeFileName = open(textfilename, 'a')
            theDict[theName] = writeFileName
        counter = counter + 1

我在上面的代码中添加了一些时间戳,它不存在,但你可以看到下面的输出。我看到的问题是每跑 1k 次它就会越来越高。第一次是 2 分钟,然后是 3 分钟,然后是 5 分钟,然后是 7 分钟。当它达到 50k 时,我担心它会花费一个小时左右,而且会花费太长时间。

1000
2016-02-25 15:15:08
20002016-02-25 15:17:07
30002016-02-25 15:20:52
2016-02-25 15:25:28
4000
2016-02-25 15:32:00
5000
2016-02-25 15:40:02
6000
2016-02-25 15:51:34
7000
2016-02-25 16:03:29
8000
2016-02-25 16:18:52
9000
2016-02-25 16:35:30
10000

有些事情我应该说清楚.. 我不知道值的名称提前,也许我应该运行并在单独的 python 脚本中获取它们以加快速度?

其次,我需要一个由逗号分隔的所有值的字符串,这就是为什么我把它放到一个文本文件中以便以后抓取。我正在考虑按照向我建议的列表来做,但我想知道这是否会有同样的问题。我认为这个问题与阅读excel有关。无论如何,我可以从中得到一个用逗号分隔的字符串,我可以用另一种方式来做。

或者也许我可以尝试/捕获而不是每次都搜索文件,如果出现错误,我可以假设创建一个新文件?也许每次查找都让它变得很慢?如果文件存在?

这个问题是我原来在这里的延续,我从那里得到了一些建议....What is the fastest performance tuple for large data sets in python?

【问题讨论】:

  • 您的代码缩进是错误的,这意味着我们无法判断您的循环的结构。你为什么要遍历单元格,然后无论如何访问某行 B 列中的单元格?我猜ws["B" + str(counter)].value 以线性时间运行。 counter,不是固定时间。
  • no 缩进没有错。你明白了..我正在获取两个值。 B 是“价值”所在的地方。 “cell.value”是名称。我正在使用计数器来抓取彼此在同一行的那个。
  • 哦,是的,你是对的.. 行中的单元格后面应该有一个缩进.. 我会修复它
  • 不知道你想做什么。提供输入电子表格的几行/列以及一两个输出文件的外观可能会有所帮助。另外,你打开了多少文件?
  • 性能应该是线性的。如果不是这种情况,那么您可能在某处有一个嵌套循环。

标签: python excel performance openpyxl


【解决方案1】:

我认为您要做的是从行的 B 列中获取一个键,并将其用作要附加的文件名。让我们加快速度:

from collections import defaultdict
Value_entries = defaultdict(list) # dict of lists of row data

for row in ws.iter_rows(rowRange):
    key = row[1].value

    Value_entries[key].extend([cell.value for cell in row])

# All done. Now write files:
for key in Value_entries.keys():
    with open(key + '.txt', 'w') as f:
        f.write(','.join(Value_entries[key]))

【讨论】:

  • 不需要计算A1样式坐标。 ws.cell(…) 接受行和列的数字(从 1 开始)值。但是您不需要在 iter_rows 中,因为您可以依赖枚举或简单索引:假设范围从“A”开始,来自“B”的单元格将是行 [1]。
  • key = row[1].value IndexError: tuple index out of range
  • 你是对的..我很想从 B 中提取一个值..我看到你正在使用 row[1].value.. 虽然我不确定它为什么会带来一个错误
  • 好吧..我看到它只能拉行[0],并且只能拉出行范围内的值。例如,我的行范围是... rowRange = "K2:K"+ str(4000).. 那是前 2k 条记录,所以我将第 K 行拉出....这就是它带来的原因和错误..这就是为什么我用骇人听闻的方式抓住 B.. 你认为最好打开范围吗?
  • 最好做任何能快速完成工作的事情。尝试两种方式计时,看看哪一种跑得更快。
【解决方案2】:

看起来您只需要 B 列中的单元格。在这种情况下,您可以使用ws.get_squared_range() 来限制要查看的单元格数量。

for row in ws.get_squared_range(min_col=2, max_col=2, min_row=1, max_row=ws.max_row):
    for cell in row: # each row is always a sequence
         filename = cell.value
         if os.path.isfilename(filename):
              …

目前尚不清楚您的代码的 else 分支发生了什么,但您可能应该在完成后立即关闭所有打开的文件。

【讨论】:

  • else 分支是如果文件不存在,它会创建文件,然后将其保持打开状态,以便我可以继续添加“
  • ,"到每个文件的末尾,这样就不必每次都重新打开文件,它只是让它们保持打开状态,大约有20个文件将打开
  • 我建议您使用标准库中的配置文件模块来找出您的代码大部分时间花在哪里,而不是在这里提供一个不完整的带有时间戳的脚本。
  • 老兄,那太紧了..我不知道他们有那个
  • 有没有超级快速的方法来设置它?我看到的一切似乎都很复杂
【解决方案3】:

根据您链接到的另一个问题和上面的代码,您似乎有一个名称-值对的电子表格。 A列中的名称和B列中的值。一个名称可以在A列中出现多次,并且每次在B列中可以有不同的值。目标是创建每个名称显示的所有值的列表。

首先,对上面代码的几点观察:

  1. counter 从未初始化。大概是初始化为1。

  2. open(textfilename,...) 被调用两次,中间没有关闭文件。调用 open 会分配一些内存来保存与文件操作相关的数据。为第一次打开调用分配的内存可能要到很久以后才会被释放,也许直到程序结束。完成文件后关闭文件是更好的做法(请参阅使用 open 作为上下文管理器)。

  3. 循环逻辑不正确。考虑:

内循环的第一次迭代:

for cell in row:                        # cell refers to A1
    valueLocation = "B" + str(counter)  # valueLocation is "B1"
    value = ws[valueLocation].value     # value gets contents of cell B1
    name = cell.value                   # name gets contents of cell A1
    textfilename = name + ".txt"
    ...
    opens file with name based on contents of cell A1, and
    writes value from cell B1 to the file
    ...
    counter = counter + 1                        # counter = 2

但是每一行至少有两个单元格,所以在内循环的第二次迭代中:

for cell in row:                          # cell now refers to cell B1
    valueLocation = "B" + str(counter)    # valueLocation is "B2"
    value = ws[valueLocation].value       # value gets contents of cell B2
    name = cell.value                     # name gets contents of cell B1
    textfilename = name + ".txt"
    ...
    opens file with name based on contents of cell "B1"  <<<< wrong file
    writes the value of cell "B2" to the file            <<<< wrong value
    ...
    counter = counter + 1        # counter = 3 when cell B1 is processed

对 50K 行中的每一行重复。根据 B 列中有多少唯一值,程序可能会尝试打开数百或数千个文件(基于单元格 A1、B1、A2、B2 的内容...)==>> 非常慢或程序崩溃。

  1. iter_rows() 返回行中单元格的元组。

  2. 正如人们在另一个问题中所建议的那样,使用字典和列表来存储值并在最后将它们全部写出来。像这样(我使用的是python 3.5,所以如果你使用的是2.7,你可能需要调整它)

这是一个直接的解决方案:

from collections import defaultdict

data = defaultdict(list)

# gather the values into lists associated with each name
# data will look like { 'name1':['value1', 'value42', ...],
#                       'name2':['value7', 'value23', ...],
#                       ...}
for row in ws.iter_rows():
    name = row[0].value
    value = row[1].value
    data[name].append(value)

for key,valuelist in data.items():
    # turn list of strings in to a long comma-separated string
    # e.g., ['value1', 'value42', ...] => 'value1,value42, ...'
    value = ",".join(valuelist)

    with open(key + ".txt", "w") as f:
        f.write(value)

【讨论】:

  • 您好,很抱歉错过了我的代码,写得很好,我现在会尝试一些。但是,当我向下工作时,一个观察结果是单元格值,我为高于此值的所有值设置了一个范围,但我没有发布。所以我在底部设置了一个 D1:D50000 的范围。所以单元格值不断命中每个单元格值,然后每次根据计数器自动生成 B 值
  • 为什么使用 append 而不是 extend?
  • append 将单个项目添加到列表的末尾。 extend 用于添加多个项目(例如项目列表)。这里我们添加单个值,所以append 是合适的。
  • 如果我们添加的值是一个 IP,那么它就像 123.123.123
  • IP 仍然是一个单一的“东西”。 IIRC excel 文件中的值是字符串。使用 extend 将单独附加每个字母而不是一次附加整个字符串: [...'1', '2', '3', '.', '1', ...] 而不是 [ ..., '123.123.123', ...]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多