【问题标题】:What is the fastest performance tuple for large data sets in python?python中大型数据集的最快性能元组是什么?
【发布时间】:2016-06-07 14:44:50
【问题描述】:

现在,我基本上是在浏览 Excel 表格。

我有大约 20 个名字,然后我有 50k 总值与这 20 个名字之一匹配,所以 excel 表有 50k 行长,B 列显示任何随机值,A 列显示 20 个名字之一。

我正在尝试为每个显示所有值的名称获取一个字符串。

Name A: 123,244,123,523,123,5523,12505,142... etc etc. 
Name B: 123,244,123,523,123,5523,12505,142... etc etc. 

现在,我创建了一个遍历 excel 表的字典,检查字典中的名称是否已准备好,如果是,则执行

strA = strA + "," + foundValue

然后它将 strA 插入到该特定名称的字典中。如果名称不存在,它会创建该字典键,然后将该值添加到其中。

现在,一开始一切都很好。但它已经大约 15 或 20 分钟,到目前为止它只添加到字典中的 5k 值,并且随着时间的推移它似乎变得更慢并且它继续运行。

我想知道是否有更好的方法或更快的方法来做到这一点。我正在考虑每 1k 个值构建一个新字典,然后最后将它们组合在一起.. 但这将是总共 50 个字典,听起来很复杂.. 虽然可能不是.. 我不确定,也许它可以更好地工作这样,这似乎行不通。

我确实需要在每个值之间用逗号显示每个值的字符串。这就是为什么我现在正在做字符串的事情。

【问题讨论】:

  • 你如何确定它只处理了 5k 个值?您是否在循环的每次迭代中都执行打印语句?
  • 是的..我在每次迭代时都做一个打印声明..现在它只有 5.5k..最后 500 个永远不会被占用
  • 现在每个条目需要 2 秒。在 30 秒内达到 500 之前,总价值仅为 1500
  • 听起来您一次又一次地循环访问相同的数据。是否可以粘贴遍历excel表格并添加到字典的代码?
  • 请问词典建好后做什么?这是相关的,因为除非某些代码需要整个 dict,否则不构建 dict 而是直接写入文件然后根据需要使用文件可能更明智(并且可能更快)。

标签: python dictionary


【解决方案1】:

此答案基于 OP 对我的评论的回答。我问他将如何处理 dict,暗示他可能一开始就不需要构建它。 @simon 回复:

我将其添加到 Excel 工作表中,因此我使用 KEY,即名称,然后 把它放在A1中,然后我取VALUE,即 1345,345,135,346,3451,35.. 等,然后将其放入 A2。然后我做 我的其余编程与该信息......但我需要 那些用逗号分隔的值,并且可以在该 excel 表中访问 就这样!

所以看起来字典毕竟不需要构建。这是一个替代方法:为每个名称创建一个文件,并将这些 文件 存储在 dict:

files = {}
name = 'John'  # let's say
if name not in files:
    files[name] = open(name, 'w')

然后,当您遍历 50k 行 excel 时,您会执行以下操作(伪代码):

for row in 50k_rows:
    name, value_string = rows.split()  # or whatever
    file = files[name]
    file.write(value_string + ',')  # if already ends with ',', no need to add

由于您的 value_string 已经用逗​​号分隔,因此您的文件将类似于 csv,无需您进行任何进一步的调整(除非您可能想在完成后去掉最后一个尾随逗号)。然后,当您需要 John 的值时,只需 value = open('John').read()

现在我从未使用过 50k 行的 excel,但如果这并不比您目前拥有的快一点,我会感到非常惊讶。拥有持久数据也是(嗯,也许)一个加分项。


编辑:

以上是面向内存的解决方案。写入文件比附加到列表要慢得多(但可能仍然比重新创建许多大字符串要快)。但是如果列表很大(看起来很可能)并且您遇到了内存问题(不是说会),您可以尝试使用文件方法。

另一种方法,类似于性能列表(至少对于我尝试的玩具测试)是使用StringIO

from io import StringIO  # python 2: import StringIO import StringIO

string_ios = {'John': StringIO()}  # a dict to store StringIO objects
for value in ['ab', 'cd', 'ef']:
    string_ios['John'].write(value + ',')
print(string_ios['John'].getvalue())

这将输出'ab,cd,ef,'

【讨论】:

  • 所以不需要字典,因为你使用的是字典?
  • @StefanPochmann 我说不需要 the 字典,一个 OP 正在尝试构建。我没有说“不需要/任何字典”。
  • 我知道,但我的意思是你的答案就是这样。你只是在说“毕竟不需要构建字典”,然后你就去构建另一个。没有解释为什么这种方法应该更好。
  • @StefanPochmann 我说“如果这不是快一点,我会感到非常惊讶”。这是否可以解释为什么它应该更好?至于为什么我认为它应该更快:与为什么 OP 当前方法较慢的原因相同,您和其他人已经很好地解释了这一点,我认为没有理由重复。
  • @simon 将 50k 行 ''qwertyuiopasdfghjklzxcvbnm,' 写入文件在我的机器上花费不到 0.04 秒,与 timeit 计时。
【解决方案2】:

取决于您如何读取 excel 文件,但假设行被读取为分隔符分隔的元组或其他内容:

d = {}
for name, foundValue in line_tuples:
    try:
        d[name].append(foundValue)
    except KeyError:
        d[name] = [foundValue]
d = {k: ",".join(v) for k, v in d.items()}

或者使用pandas:

import pandas as pd
df = pd.read_excel("some_excel_file.xlsx")
d = df.groupby("A")["B"].apply(lambda x: ",".join(x)).to_dict()

【讨论】:

  • 好吧,我只是把它当作字典来做,这样它就像一个结构化的东西,就像每个名字都有一个键,然后每个名字都有一个值,只是一种跟踪一切的方法,就像我想这样做的方式一样
  • 好的。在我的回答中尝试上述方法。一些通用的cmets。打印到标准输出会使执行速度非常慢,特别是对于大量打印。还要查看 try/except,它可以避免检查密钥是否存在 50 次(基本上做然后请求宽恕总是比请求许可然后做更快)。在这种情况下,你会有类似 `try d[k] += ","+foundvalue except KeyError: d[k]=foundvalue
  • 对于大型数据集.. 你怎么知道它当时走了多远或运行速度有多快?我在每个 for 循环上都打印出来,它运行得很快,直到达到 3k 左右。我不确定是因为我每次都打印还是其他原因
  • timeit 模块的答案有多快。多远取决于方法。在纯 python 方法(第一个解决方案)中,您可以打印一些东西,但当然不要在每一步都打印,而是在您的情况下每 100/500/1000 打印一次。印刷品非常昂贵。对于熊猫的答案,我肯定会尝试。特别是对于大型数据集,您希望使用矩阵运算而不是元素运算,它们是优化的 goto 事情。但在那种情况下,你不知道你已经走了多远,因为在矩阵运算中没有这个概念。
  • 啊,是的,还有一件事,监控你的记忆。除了打印减慢执行速度之外,还可能是您开始使用堆内存而不是 RAM。
【解决方案3】:

正确的方法是在列表中收集并在最后加入,但如果由于某种原因你想使用字符串,你可以加快字符串扩展的速度。将字符串从 dict 中弹出,以便只有一个对其的引用,从而可以启动优化。

演示:

>>> timeit('s = d.pop(k); s = s + "y"; d[k] = s', 'k = "x"; d = {k: ""}')
0.8417842664330237

>>> timeit('s = d[k];     s = s + "y"; d[k] = s', 'k = "x"; d = {k: ""}')
294.2475278390723

【讨论】:

    【解决方案4】:

    有很多因素可能会导致您的程序运行缓慢。

    String concatenation in python 与大字符串一起使用时效率极低。

    Python 中的字符串是不可变的。这个事实经常偷偷摸摸,让 Python 新手程序员一头雾水。不变性带来了一些优点和缺点。在加号列中,字符串可以用作字典中的键,并且可以在多个变量绑定之间共享单个副本。 (Python 自动共享一个和两个字符的字符串。)在减号列中,您不能说“将任何给定字符串中的所有 'a's 更改为 'b's”。相反,您必须创建一个具有所需属性的新字符串。这种持续复制会导致 Python 程序的效率显着降低。

    考虑到示例中的每个字符串可能包含数千个字符,每次进行连接时,python 都必须将该巨大的字符串复制到内存中以创建一个新对象。

    这样会更有效率:

    strings = []
    strings.append('string')
    strings.append('other_string')
    ...
    ','.join(strings)
    

    在您的情况下,它应该存储一个列表,而不是每个字典键都存储一个大字符串,并且您只需将每个匹配项附加到列表中,并且只有在最后您才会使用 str.join 进行字符串连接.

    另外,printing to stdout is also notoriously slow。如果您在大规模 50,000 项循环的每次迭代中都打印到标准输出,那么每次迭代都会被无缓冲的标准输出写入所阻碍。考虑只打印每个nth 迭代,或者改为写入文件(文件写入通常被缓冲),然后从另一个终端拖尾文件。

    【讨论】:

    • 字符串连接,带有一个列表,我需要列表中的每个值用逗号分隔,我不需要一遍又一遍地做同样的事情吗?
    • 因为从技术上讲,列表中已经有一堆逗号...我想知道是否可以将列表粘贴在某个地方?那么那样的话,我会把我的整个事情用一个列表分开吗?我将每个逗号分隔值粘贴到 Excel 表中
    【解决方案5】:

    不要构建一个看起来像列表的字符串,而是使用一个实际的列表,并在完成后用它来表示你想要的字符串。

    【讨论】:

    • 那么制作 20 个不同的列表?但如果我做整个事情,情况会不会完全一样... strA = strA + "," + currentListValue
    • 制作也太慢了吧?还是字典让这一切变慢了?
    • 通过重复连接构建字符串比通过重复附加构建列表要慢。
    • 但是我需要一个字符串,它是一个特定名称的整个列表长度。所以我得到的是如果我需要这样做,如果我这样做会不会一样慢来自 20 个不同的列表?不知道,你怎么看?
    • 无论如何,这甚至不能像现在这样工作,此时它太慢了,似乎需要很长时间才能完成,而且只有 5.5k
    猜你喜欢
    • 2018-04-02
    • 1970-01-01
    • 2014-12-19
    • 1970-01-01
    • 2015-10-31
    • 2021-01-05
    • 1970-01-01
    • 2012-03-03
    • 2021-11-12
    相关资源
    最近更新 更多