现在,我基本上正在浏览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个词典,这听起来很复杂......虽然可能不是......我不确定,也许它可以这样做得更好,这似乎行不通。
我需要在每个值之间用逗号显示每个值的字符串。这就是我现在正在做弦乐的原因。
答案 0 :(得分:2)
有很多事情可能导致程序运行缓慢。
与大字符串一起使用时,String concatenation in python效率极低。
Python中的字符串是不可变的。这个事实经常偷偷摸摸并咬住了新手Python程序员。不变性赋予一些优点和缺点。在加号列中,字符串可以用作字典中的键,单个副本可以在多个变量绑定之间共享。 (Python自动共享单字符和双字符字符串。)在减号列中,您不能说任何类似的内容,“在任何给定字符串中将所有'a'更改为'b'”。相反,您必须创建具有所需属性的新字符串。这种不断的复制可能会导致Python程序的效率低下。
考虑到示例中的每个字符串可能包含数千个字符,每次进行连接时,python都必须将该巨型字符串复制到内存中以创建新对象。
这会更有效率:
strings = []
strings.append('string')
strings.append('other_string')
...
','.join(strings)
在你的情况下,不是每个字典键存储一个大字符串,它应该存储一个列表,你只需将每个匹配附加到列表中,并且只在最后使用{{1进行字符串连接}}
此外,printing to stdout is also notoriously slow。如果您在大量50,000项循环的每次迭代中打印到stdout,则每次迭代都会被无缓冲的写入stdout所阻止。考虑只打印每str.join
次迭代,或者可能写入文件(文件写入通常是缓冲的),然后从另一个终端拖尾文件。
答案 1 :(得分:1)
这个答案是基于OP对我评论的回答。我问他会怎么做这个词,暗示他可能不需要首先建立它。 @simon回复:
我把它添加到excel表,所以我拿了KEY,这是名字,和 把它放在A1,然后我拿VALUE,这是 1345,345,135,346,3451,35等等,并把它放入A2。那我呢 我的其余编程信息......但我需要 那些由逗号分隔的值和excel表中的不可访问的值 那样的!
所以看起来dict不一定要构建。以下是另一种选择:对于每个名称,创建一个文件,并将这些文件存储在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-like而不需要进一步调整(除非您想在完成后删除最后一个尾随逗号)。然后当你需要John的值时,只需要value = open('John').read()
。
现在我从来没有使用过50k排的擅长,但是如果这比现在的速度要快得多,我会感到非常惊讶。拥有持久数据也是(好吧,也许)一个加号。
编辑:
以上是面向内存的解决方案。写入文件比附加到列表要慢得多(但可能仍然比重新创建许多大字符串更快)。但是如果列表很大(看起来很可能)并且你遇到内存问题(不会说你会),你可以试试文件方法。
替代方案,类似于性能列表(至少对于我尝试过的玩具测试)是使用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,'
答案 2 :(得分:0)
不是构建一个看起来像列表的字符串,而是使用一个实际的列表,并在完成时从中获取所需的字符串表示。
答案 3 :(得分:0)
取决于你如何阅读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()
答案 4 :(得分:0)
正确的方法是收集列表并在最后加入,但如果由于某种原因你想使用字符串,你可以加快字符串扩展。从字典中弹出字符串,以便只有一个引用它,因此优化可以启动。
演示:
>>> 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