【问题标题】:Study of most efficient method to convert tree structure to csv in PythonPython中将树结构转换为csv的最有效方法研究
【发布时间】:2018-03-02 21:37:32
【问题描述】:

我有一棵树,结构如下:

my_hash_pop = {
    "Europe" : {
        "France" : {
            "Paris" : 2220445,
            "Lille" : 225789,
            "Lyon" : 506615 },
        "Germany" : {
            "Berlin" : 3520031,
            "Munchen" : 1544041,
            "Dresden" : 540000 },
        },
    "South America" : {
        "Brasil" : {
            "Sao Paulo" : 11895893,
            "Rio de Janeiro" : 6093472 },
        "Argentina" : {
            "Salta" : 535303,
            "Buenos Aires" : 3090900 },
        },
    }

我想使用 python 将此结构转换为 CSV:

Europe;Germany;Berlin;3520031
Europe;Germany;Munchen;1544041
Europe;Germany;Dresden;540000
Europe;France;Paris;2220445
Europe;France;Lyon;506615
Europe;France;Lille;225789
South America;Argentina;Buenos Aires;3090900
South America;Argentina;Salta;3090900
South America;Brasil;Sao Paulo;11895893
South America;Brasil;Rio de Janeiro;6093472

由于我的树在现实生活中包含大量叶子(显然不在此示例中),因此我使用的转换脚本需要很长时间。我试图找到一种更有效的方式来进行转换。这是我尝试过的:

第一种方法:在每个叶子上连接字符串:

### METHOD 1 ###

start_1 = time.time()

data_to_write = ""

for region in my_hash_pop:
    for country in my_hash_pop[region]:
        for city in my_hash_pop[region][country]:
            data_to_write += region+";"+country+";"+city+";"+str(my_hash_pop[region][country][city])+"\n"

filename = "my_test_1.csv"
with open("my_test_1.csv", 'w+') as outfile:
    outfile.write(data_to_write)
    outfile.close()

end_1 = time.time()
print("---> METHOD 1 : Write all took " + str(end_1 - start_1) + "s")

第二种方法:用“检查点”连接字符串

### METHOD 2 ###

start_2 = time.time()

data_to_write = ""

for region in my_hash_pop:
    region_to_write = ""

    for country in my_hash_pop[region]:
        country_to_write = ""

        for city in my_hash_pop[region][country]:
            city_to_write = region+";"+country+";"+city+";"+str(my_hash_pop[region][country][city])+"\n"
            country_to_write += city_to_write

        region_to_write += country_to_write

    data_to_write += region_to_write

filename = "my_test_2.csv"
with open("my_test_2.csv", 'w+') as outfile:
    outfile.write(data_to_write)
    outfile.close()

end_2 = time.time()
print("---> METHOD 2 : Write all took " + str(end_2 - start_2) + "s")

第三种方法:使用 Writer 对象

### METHOD 3 ###

import csv

start_3 = time.time()

with open("my_test_3.csv", 'w+') as outfile:
    del_char = b";"
    w = csv.writer(outfile, delimiter=del_char)

    for region in my_hash_pop:
        for country in my_hash_pop[region]:
            for city in my_hash_pop[region][country]:
                w.writerow([region, country, city, str(my_hash_pop[region][country][city])])

end_3 = time.time()
print("---> METHOD 3 : Write all took " + str(end_3 - start_3) + "s")

比较每种方法在我的示例树的成长过程中所花费的时间,我注意到方法 1 相当低效。但是,在方法 2 和 3 之间,结果会有所不同,并且不是那么明显(通常方法 3 似乎更有效)

因此我有两个问题:

  • 您看到我可能想尝试的另一种方法吗?
  • 有没有更好的方法来检查和比较不同方法的效率?

还有一个奖励:

  • 我注意到方法 1 和 2 的输出文件大小完全相同。方法 3 的输出文件比其他两个大。为什么?

感谢您的贡献!

【问题讨论】:

    标签: python json csv tree


    【解决方案1】:

    第三种方法最有前途。

    您可以通过在每个级别使用 items() 来避免许多 dict 查找:

    with open("my_test_3.csv", 'w+') as outfile:
        del_char = ";"
        w = csv.writer(outfile, delimiter=del_char)
    
        for region,countries in my_hash_pop.items():
            for country,cities in countries.items():
                for city,value in cities.items():
                    w.writerow([region, country, city, value])
    

    示例 2 和 3 之间的大小差异来自 newlines:"\n" 对应于 'my_test_2.csv',"\r\n" 对应于 'my_test_3.csv'。 所以'my_test_3.csv' 中的每一行都比'my_test_2.csv' 中的大1 个字节。

    【讨论】:

    • 很好地捕捉到文件之间的大小差异!我会试试 ''.items'' 并告诉你它对时间的改善程度。
    • 这个方法确实比我用的效率高,谢谢。它与@QuantumEnergy 花费的时间完全相同
    • @Mago 当然。 Quantum 使用完全相同的循环,但包含在嵌套列表理解中。
    【解决方案2】:
    start_1 = time.time()
    filename = "my_test_4.csv"
    with open("my_test_4.csv", 'w+') as outfile:
        a = [outfile.write("%s;%s;%s;%s\n" % (k, kk, kkk, vvv))
             for (k, v) in my_hash_pop.items()
             for (kk, vv) in v.items()
             for (kkk, vvv) in vv.items()]
    end_1 = time.time()
    print("---> METHOD 1 : Write all took " + str(end_1 - start_1) + "s")
    

    【讨论】:

    • 谢谢,它更高效,而且与@eric-duminil 的回答所花费的时间完全相同
    • 你不需要,是吗?
    • 是的,不需要a!
    【解决方案3】:

    建议使用 pandas,如下所示:

    import pandas as pd
    df = pd.DataFrame([(i,j,k,my_hash_pop[i][j][k])
                               for i in my_hash_pop.keys() 
                               for j in my_hash_pop[i].keys()
                               for k in my_hash_pop[i][j].keys()])
    
    with open("my_test_4.csv", 'w') as outfile:
        outfile.write(df.to_csv(sep=';', header=False, index=False)))
    

    我没有比较执行时间,也许你不适合使用 pandas,所以这只是一个建议。

    【讨论】:

    • 我似乎无法在我的环境中安装pandas 模块,但我会尝试一个更开放的模块并通知您。
    【解决方案4】:

    panads 在处理大型数据集时非常有效。下面是在 pandas 中导入 dict 的 dict 的方法,使用 json_normalize 展平,然后您可以对其进行操作。例如写入 csv 等。

    让我知道你的选择如何。

    源代码

    from pandas.io.json import json_normalize
    
    df = json_normalize(my_hash_pop)
    
    outfile = "temp.csv"
    del_char = ";"
    
    with open(outfile, 'wb+') as outfile:
        w = csv.writer(outfile, delimiter =';',quoting=csv.QUOTE_MINIMAL)
        for i in df.keys():
            s = ("{};{}").format(i.replace('.',';'),df[i][0]).split(";")
            w.writerow(s)
    

    【讨论】:

    • 我似乎无法在我的环境中安装pandas 模块,但我会尝试一个更开放的模块并让您知道。谢谢
    猜你喜欢
    • 2015-04-14
    • 2017-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-11
    • 1970-01-01
    • 2021-09-16
    • 2015-09-22
    相关资源
    最近更新 更多