Python中将树形结构转换为CSV文件的最有效方法研究

4

我有一棵树,其结构如下:

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. 是否有更好的方法来检查和比较不同方法的效率?
还有一个额外的问题:
我注意到方法1和方法2的输出文件大小完全相同。方法3的输出文件比另外两种方法要大。这是为什么呢?
感谢任何帮助!
4个回答

1
第三种方法最有前途。
您可以通过在每个级别使用items()来避免许多字典查找:
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'',然后告诉你它对时间的改善有多大。 - Mago
这个方法确实比我用的那个更有效率,所以非常感谢。它花费的时间与@QuantumEnergy完全相同。 - Mago
@Mago 确定。Quantum使用完全相同的循环,但是打包在嵌套的列表推导中。 - Eric Duminil

1
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的答案完全一样 - Mago
你不需要a,对吧? - Eric Duminil
是的,不需要 a - QuantumEnergy

0
一个建议是使用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”模块,但我会尝试在一个更开放的环境中安装并让您知道。 - Mago

0

pandas 在处理大型数据集时非常高效。以下是一种将字典导入 pandas 的方法,使用 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”模块,但我会尝试在一个更开放的环境中安装并让您知道。谢谢。 - Mago

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接