2014-10-06 41 views
1

我們正在運行一個已經花費了一個小時才能執行的程序(並且還沒有完成),所以我們想知道如果我們能夠改進我們的程序代碼,跑得更快。如何在Python中高效地預處理和解析JSON數據

我們的程序由兩部分組成:首先我們需要更改字符串,因爲我們使用JSON字典,並且所有項目的數據都具有相似的鍵(「軌道」) - 如果我們不這樣做,輸出會給出只有第一首歌曲。其次,我們需要將JSON數據打印到csv文件。

JSON文件的片段:(實際的文件是大約900 MB)

{
「VV」:{
「版本」:1, 「數據」:[
{
「軌道」:[
{
「時間」: 「YYYY-MM-DDTHH:MM:SS:MS」, 「TL」:{
「×」:10, 「Y」:11 }, 「BR」:{
「×」:20, 「Y」:20 } }, {
「時間」:「YYYY-MM-DDTHH:MM :SS:MS」, 「TL」:{
「×」:12, 「Y」:15 }, 「BR」:{
「×」:22, 「Y」:23 } } ], 「track」:[
{
「時間」: 「YYYY-MM-DDTHH:MM:SS:MS」, 「TL」:{
「×」:30, 「Y」:39 }, 「BR」: {
「×」:40, 「Y」:45 } }, {
「時間」: 「YYYY-MM-DDTHH:MM:SS:MS」, 「TL」:{
「x」:12, 「y」:18 }, 「br」:{
「×」: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) 

在此先感謝您。

+0

我想了解你的問題。首先,你需要用'tr'替換'track'。其次,你需要轉儲一個CSV文件。你想優化過程。是嗎? – Raiyan 2014-10-06 13:56:05

+0

是的,那正是我們的問題。我們需要用tr0,tr1,tr2等替換軌道,因爲我們需要相同的密鑰。 – Marjolein 2014-10-06 14:01:57

+0

你能改變上游輸出格式嗎?例如,每行輸出一個軌道(比如推文流)而不是創建一個大的json對象 – jfs 2014-10-06 14:09:12

回答

0

嘗試這樣的: -

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

我很抱歉,但是如何改進我們的程序代碼? – Marjolein 2014-10-06 13:58:27

+1

我不認爲這個答案是解決OP的問題 – Raiyan 2014-10-06 14:06:42

+0

感謝您的所有意見。但只是修改了json的解析。真的不知道這是否有助於操作 – 2014-10-06 14:10:10

3

的第一件事是衡量當前的代碼的時候表現:從輸入提取短小代表性的數據樣本,這樣就可以在一對夫婦的運行基準秒(一分鐘,上衣)。保存你的代碼產生的輸出,以測試你以後沒有損壞任何東西。

首先我們需要改變的字符串,因爲我們使用JSON字典和 數據也有類似的鍵(「跟蹤」)的所有項目 - 如果我們不這樣做 這一點,只輸出給人的第一跟蹤。

第二件事是避免更改字符串(刪除代碼的第一部分)。在最壞的情況下(如果您的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

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) 

每次更改之後,再次測量時的性能和檢查輸出仍然是正確的。如果使用multidict()而不是字符串替換並沒有改善的時間性能,那麼你可以嘗試濫用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], 
      ]) 

爲了增強可讀性,你可以重寫第二部分在內存中的整個JSON對象:

#!/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() 
+0

非常感謝!它加快了我們的代碼! – Marjolein 2014-10-06 15:19:21

+0

@ Marjolein:我已經添加了一個軌道變體('write_tracks()') – jfs 2014-10-06 15:23:20

+0

@Marjolein:我改變了代碼以使用stdin/stdout來方便時間測量/測試。 – jfs 2014-10-06 15:34:05

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) 

你也可以只使用非Python程序一樣sed,只是創建該文件的副本替換了所有出現。

第二:我認爲搜索裸露的單詞「track」並不聰明。你可能會匹配實際的數據。我至少會嘗試將它匹配爲JSON密鑰並搜索/替換爲像"track":[這樣的字符串。

據說,閱讀和操縱一個900MB的文件將花費大量的內存。我可能會嘗試在命令行上用sed來做到這一點,或試圖找出一種方法來做到這一點的數據塊,並閱讀一行+逐行寫一行。我認爲這是所有的一行,所以你不能用readlines()迭代文件描述符,並且必須處理字節範圍。我從來沒有必須處理之前的數據流分析,因此無法提供有關這方面的任何見解。