如何有效地预处理和解析JSON数据?

时间:2014-10-06 13:41:59

标签: python json csv dictionary replace

我们目前正在运行一个已花费一个小时执行的程序(但仍未完成),因此我们想知道我们是否可以改进程序代码以便它运行得更快。

我们的程序由两部分组成:首先我们需要更改字符串,因为我们使用JSON字典,并且数据具有相似的键(" track")用于所有项目 - 如果我们不这样做的话这样做,输出只给出第一个轨道。其次,我们需要将JSON数据打印到csv文件。

JSON文件的片段:(实际文件大约为900 MB)

{
 "VV":{
    "Version":1,
    "Data":[
       {
          "track":[
             {
                "time":"YYYY-MM-DDTHH:MM:SS:MS",
                "tl":{
                   "x":10,
                   "y":11
                },
                "br":{
                   "x":20,
                   "y":20
                }
             },
             {
                "time":"YYYY-MM-DDTHH:MM:SS:MS",
                "tl":{
                   "x":12,
                   "y":15
                },
                "br":{
                   "x":22,
                   "y":23
                }
             }
          ],
          "track":[
             {
                "time":"YYYY-MM-DDTHH:MM:SS:MS",
                "tl":{
                   "x":30,
                   "y":39
                },
                "br":{
                   "x":40,
                   "y":45
                }
             },
             {
                "time":"YYYY-MM-DDTHH:MM:SS:MS",
                "tl":{
                   "x":12,
                   "y":18
                },
                "br":{
                   "x":22,
                   "y":24
                }
             }
          ]
         }
      ]
   }
}

我们代码的第一部分:

with open(r'filename.json') as json_file:
    fil = json_file.read()
    i = 0
    print i
    while ('track' in fil) :
        fil = fil.replace('track', 'tr'+str(i), 1)
        i = i + 1
    print i
    input_data = json.loads(fil)

data_d = input_data['VV']['Data'][0]

第二部分:

with open(r'output.csv', 'wb') as csv_file:
    writer = csv.writer(csv_file)
    i = 0
    for track, data in data_d.items():
    i = i+1 # Track

    for item in data:

        #TRACK
        item_values = []
        item_values.append(i)

        #DAY
        #print item['time']
        day = item['time'][8:10]
        item_values.append(day)

        #COORDINATEN
        item_values.append(item['tl']['x'])
        item_values.append(item['tl']['y'])
        item_values.append(item['br']['x'])
        item_values.append(item['br']['y'])

        #TIME
        time = item['time'][11:13]+item['time'][14:16]+item['time'][17:19]+item['time'][20:23]
        item_values.append(time)

        writer.writerow(item_values)

3 个答案:

答案 0 :(得分:3)

首先要测量当前代码的时间性能:从输入中提取一个小的代表性数据样本,以便您可以在几秒钟(一分钟,顶部)运行基准测试。保存代码生成的输出,以测试您以后没有破坏任何内容。

  

首先我们需要更改字符串,因为我们使用JSON字典和   对于所有项目,数据都有类似的键(" track") - 如果我们不这样做的话   这个,输出只给出第一个轨道。

第二件事是避免更改字符串(删除代码的第一部分)。在最坏的情况下(如果您的900M文件实际上不是json文件,因为json格式不支持json对象中的重复名称:"When the names within an object are not unique, the behavior of software that receives such an object is unpredictable."),您可以使用类似multidict() solution的内容大文件,例如,如果你使用python2然后避免创建一个不需要的列表的.items()调用,你可以改用.iteritems(),避免复制dict(d),只返回defaultdict:< / p>

import json
from collections import defaultdict

def multidict(ordered_pairs):
    """Convert duplicate key values to a list."""
    # read all values into list
    d = defaultdict(list)
    for k, v in ordered_pairs:
        d[k].append(v)

    # collapse list that has only 1 item
    for k, v in d.iteritems():
        if len(v) == 1:
            d[k] = v[0]
    return d 

with open('filename.json') as json_file:
    obj = json.load(json_file, object_pairs_hook=multidict)

每次更改后,再次测量时间性能并检查输出是否仍然正确。

为了便于阅读,您可以重写第二部分:

import csv

with open('output.csv', 'wb') as csv_file:
    writer = csv.writer(csv_file)
    for i, data in enumerate(data_d.itervalues(), start=1):
        for item in data:
            t = item['time']
            writer.writerow([
                #TRACK
                i,
                #DAY
                t[8:10],
                #COORDINATEN
                item['tl']['x'],
                item['tl']['y'],
                item['br']['x'],
                item['br']['y'],
                #TIME
                t[11:13]+t[14:16]+t[17:19]+t[20:23],
            ])

如果使用multidict()代替字符串替换并未改善时间性能,那么您可以尝试滥用multidict()来更改输入格式,而无需在内存中加载整个json对象: / p>

#!/usr/bin/env python2
import json
import sys
from collections import defaultdict

def write_tracks(ordered_pairs):
    # read all values into list
    d = defaultdict(list)
    for k, v in ordered_pairs:
        d[k].append(v)

    # collapse list that has only 1 item
    for k, v in d.iteritems():
        if k == 'track':
            for tracks in v: # print one track (in json format) per line
                print("\n".join(map(json.dumps, tracks)))
            break
        elif len(v) == 1:
            d[k] = v[0]
    else: # no tracks, return the constructed object
        return d

json.load(sys.stdin, object_pairs_hook=write_tracks) # write tracks

您可以从命令行中使用它:

$ <filename.json python write_tracks.py | python convert_tracks.py >output.csv

其中convert_tracks.py类似于:

#!/usr/bin/env python2
import csv
import json
import sys

def main():
    writer = csv.writer(sys.stdout)
    for i, line in enumerate(sys.stdin, start=1):
        try:
            item = json.loads(line)
        except ValueError:
             pass # ignore errors
        else:
            t = item['time']
            writer.writerow([
                #TRACK
                i,
                #DAY
                t[8:10],
                #COORDINATEN
                item['tl']['x'],
                item['tl']['y'],
                item['br']['x'],
                item['br']['y'],
                #TIME
                t[11:13]+t[14:16]+t[17:19]+t[20:23],
            ])

if __name__ == "__main__":
    main()

答案 1 :(得分:1)

在这一行中,有两件事情向我跳出来:

while ('track' in fil) :

首先 - 每次都会执行此while循环。如果您仅仅因此而遇到性能问题,我不会感到惊讶。每次完成替换时,它都会再次搜索整个字符串。这真的很低效。

由于您只是使用文本对象而不是JSON对象,因此最好使用带有函数替换的正则表达式,或者使用其他基于正则表达式的策略,它会找到所有匹配项,然后您就可以使用它。像这样:

i = 0
def sub_track(g):
    global i
    i += 1
    return "tr_%s" % i

RE_track = re.compile('track')
RE_track.sub(sub_track, data)

您也可以使用像sed这样的非python程序,只需创建一个文件的副本,并替换掉所有出现的内容。

第二:我认为搜索“轨道”这个单词并不聪明。您可以匹配实际数据。我至少会尝试将其匹配为JSON密钥,并搜索/替换像"track":[这样的字符串。

据说,阅读和操作900MB文件会占用大量内存。我可能会尝试使用sed在命令行上执行此操作,或尝试找出在数据块中执行此操作的方法并读取一行+逐行写入一行。我假设这是一行,所以你不能用readlines()迭代文件描述符,并且必须与字节范围有关。我以前从未处理过分析数据流的问题,因此无法对此提供任何见解。

答案 2 :(得分:0)

试试这样: -

import json
from pprint import pprint
json_data=open(filename.json)
data = json.load(json_data)
pprint(data)
json_data.close()