使用“ dict of dicts”或“ dicts list”在python中存储来自CSV的数据?

时间:2019-08-19 20:22:51

标签: python python-3.x list dictionary

Python中有什么更好的选择?

  1. 一个具有10000个密钥的dict,每个密钥包含一个包含10个项目的list
  2. 一个具有10000个密钥的dict,每个密钥包含一个带有10个“子密钥”的dict
  3. 一个带有10个“子密钥”的dict,每个子密钥包含一个带有10000个密钥的dict
  4. 使用熊猫库(由@RomanPerekhrest在评论中建议)

选项2似乎比选项1对程序员更友好(例如,使用mydict['long-ID1']['street']而不是mydict['long-ID1'][3])。

但是,我担心这可能会导致不必要的开销。我不希望子键的数量或顺序(例如'street')将来会发生变化。

我正在根据性能(查找速度)寻找“最佳”选项,同时还在考虑存储空间(在RAM以及使用泡菜保存时。)

背景

我正在解析具有以下列的〜10000行(工作站)的〜4MB CSV文件:

ID -唯一〜30个字符串
名称,街道,城市... -字符串
纬度,经度-GPS坐标
日期-猜一猜
jsonstring -一些嵌套的字典

我想使用 ID 作为密钥将数据作为dict station导入python,以允许快速查找station['some-id']。然后,我将在dict中执行几百万次查找,通常只根据每个用例查看每个工作站10列中的1-2列。

这就是为什么在写这个问题时,我想到了选项3 ...我看到的缺点是10000个键比10个键长得多,因此重复10次大字典可能不是这样。关于记忆的好主意?

**更新** 基于@Giova's answer,我汇总了选项1和2的性能比较,您也可以在repl.it here上找到它们:

from timeit import timeit

def listops(n:int, l):
    for i in range(n):
        t = l[i]
    return l

def dictops(n:int, d):
    for i in range(n):
        t = d["nice_key_%1d"%i]
    return d


n= 10
l = []
for i in range(n):
    l.append(i)
d = dict()
for i in range(n):
    d["nice_key_%1d"%i] = None
t1=timeit(lambda: listops(n, l), number=1000000)
t2=timeit(lambda: dictops(n, d), number=1000000)
print("list:",t1)
print("dict:",t2)

请注意,这只是查找速度的乘积,而Giova的代码段也着眼于数据结构的创建,而我对此并不感兴趣。

结果:

list: 4.312716690998059
dict: 14.279126501001883

我想回答我问题的“存储空间”部分最简单的方法是同时实现这两个功能并检查泡菜文件的大小?

1 个答案:

答案 0 :(得分:2)

在您提到的选项之间,使用字典(2,3)的选项在访问速度方面更好。介于2到3之间的哪个更好,这实际上取决于您希望检索元素的方式。

还可以考虑将逐行数字作为第一个字典的键,通过这样操作,合理地假设字符串比小数字消耗更多的内存,这种技巧将浪费更少的内存。

仅在速度不是很重要的情况下才应考虑选项1。列表方法花费大量时间的原因。只需一个简单的代码片段就可以轻松地凭经验验证这一点:

from elapsed import TimeIt


def listops(n:int):
    l = []
    for i in range(n):
        l.append(i)
    for i in range(n):
        t = i in l
    return l


def dictops(n:int):
    d = dict()
    for i in range(n):
        d[i] = None
    for i in range(n):
        t = i in d
    return d

TimeIt(lambda: listops(10), 1000000, logger_name=__name__, msg='listops(10)')
TimeIt(lambda: dictops(10), 1000000, logger_name=__name__, msg='dictops(10)')

这里,我们有两个函数listops和'dictops',分别创建给定list整数的dictn,然后检查每个整数是否存在插入的整数。该代码基本上只希望检查listdict的构造,插入和状态测试。这将注销以下时间:

Elapsed time '1000000 times listops(10)':  3.559521 seconds.
Elapsed time '1000000 times dictops(10)':  2.720709 seconds.

[请注意,TimeIt(驼峰式保护壳)是我为实现此目的而定制的类,但是您可以从timeit模块中轻松使用timeit。]

即使不是您的问题的明确组成部分,我也建议您使用sqlite内存数据库,并在搜索查询中使用列索引,并准备按需触发准备好的语句。