【问题标题】:Python Performance Tuning: JSON to CSV, big filePython 性能调优:JSON 到 CSV,大文件
【发布时间】:2017-06-29 09:16:12
【问题描述】:

一位同事要求我将“Yelp 数据集挑战”中的 6 个大文件从有点“扁平”的常规 JSON 转换为 CSV(他认为它们看起来很有趣的教学数据) .

我想我可以用:

# With thanks to http://www.diveintopython3.net/files.html and https://www.reddit.com/r/MachineLearning/comments/33eglq/python_help_jsoncsv_pandas/cqkwyu8/

import os
import pandas

jsondir = 'c:\\example\\bigfiles\\'
csvdir = 'c:\\example\\bigcsvfiles\\'
if not os.path.exists(csvdir): os.makedirs(csvdir)

for file in os.listdir(jsondir):
    with open(jsondir+file, 'r', encoding='utf-8') as f: data = f.readlines()
    df = pandas.read_json('[' + ','.join(map(lambda x: x.rstrip(), data)) + ']')
    df.to_csv(csvdir+os.path.splitext(file)[0]+'.csv',index=0,quoting=1)

很遗憾,我的计算机内存无法胜任这种大小的文件的任务。 (即使我摆脱了循环,虽然它会在不到一分钟的时间内敲出一个 50MB 的文件,但它很难避免冻结我的计算机或在 100MB 以上的文件上崩溃,最大的文件是 3.25GB。)

我可以运行其他简单但高效的东西吗?

在循环中会很棒,但如果对内存有影响,我也可以使用单独的文件名运行 6 次(只有 6 个文件)。

这里是一个“.json”文件内容的例子——注意每个文件实际上有很多 JSON 对象,每行 1 个。

{"business_id":"xyzzy","name":"Business A","neighborhood":"","address":"XX YY ZZ","city":"Tempe","state":"AZ","postal_code":"85283","latitude":33.32823894longitude":-111.28948,"stars":3,"review_count":3,"is_open":0,"attributes":["BikeParking: True","BusinessAcceptsBitcoin: False","BusinessAcceptsCreditCards: True","BusinessParking: {'garage': False, 'street': False, 'validated': False, 'lot': True, 'valet': False}","DogsAllowed: False","RestaurantsPriceRange2: 2","WheelchairAccessible: True"],"categories":["Tobacco Shops","Nightlife","Vape Shops","Shopping"],"hours":["Monday 11:0-21:0","Tuesday 11:0-21:0","Wednesday 11:0-21:0","Thursday 11:0-21:0","Friday 11:0-22:0","Saturday 10:0-22:0","Sunday 11:0-18:0"],"type":"business"}
{"business_id":"dsfiuweio2f","name":"Some Place","neighborhood":"","address":"Strip or something","city":"Las Vegas","state":"NV","postal_code":"89106","latitude":36.189134,"longitude":-115.92094,"stars":1.5,"review_count":2,"is_open":1,"attributes":["BusinessAcceptsBitcoin: False","BusinessAcceptsCreditCards: True"],"categories":["Caterers","Grocery","Food","Event Planning & Services","Party & Event Planning","Specialty Food"],"hours":["Monday 0:0-0:0","Tuesday 0:0-0:0","Wednesday 0:0-0:0","Thursday 0:0-0:0","Friday 0:0-0:0","Saturday 0:0-0:0","Sunday 0:0-0:0"],"type":"business"}
{"business_id":"abccb","name":"La la la","neighborhood":"Blah blah","address":"Yay that","city":"Toronto","state":"ON","postal_code":"M6H 1L5","latitude":43.283984,"longitude":-79.28284,"stars":2,"review_count":6,"is_open":1,"attributes":["Alcohol: none","Ambience: {'romantic': False, 'intimate': False, 'classy': False, 'hipster': False, 'touristy': False, 'trendy': False, 'upscale': False, 'casual': False}","BikeParking: True","BusinessAcceptsCreditCards: True","BusinessParking: {'garage': False, 'street': False, 'validated': False, 'lot': False, 'valet': False}","Caters: True","GoodForKids: True","GoodForMeal: {'dessert': False, 'latenight': False, 'lunch': False, 'dinner': False, 'breakfast': False, 'brunch': False}","HasTV: True","NoiseLevel: quiet","OutdoorSeating: False","RestaurantsAttire: casual","RestaurantsDelivery: True","RestaurantsGoodForGroups: True","RestaurantsPriceRange2: 1","RestaurantsReservations: False","RestaurantsTableService: False","RestaurantsTakeOut: True","WiFi: free"],"categories":["Restaurants","Pizza","Chicken Wings","Italian"],"hours":["Monday 11:0-2:0","Tuesday 11:0-2:0","Wednesday 11:0-2:0","Thursday 11:0-3:0","Friday 11:0-3:0","Saturday 11:0-3:0","Sunday 11:0-2:0"],"type":"business"}

嵌套的 JSON 数据可以简单地保留为表示它的字符串文字——我只是希望将顶级键转换为 CSV 文件标题。

【问题讨论】:

  • 不是一次读取和解析整个文件,您可以尝试一次在一个json字典或一个csv行中读取它,然后解析并插入到csv中。它需要更多的手动编码,但在文件流样式中表现良好。

标签: python json performance csv pandas


【解决方案1】:

问题在于您的代码将整个文件读入内存,然后在内存中创建它的近似副本。我怀疑它还会创建第三个副本,但尚未验证。 Neo X 建议的解决方案是逐行读取文件并进行相应处理。这是 for 循环的替换:

for file in os.listdir(jsondir):
    csv_file = csvdir + os.path.splitext(file)[0] + '.csv'
    with open(jsondir+file, 'r', encoding='utf-8') as f, open(csv_file, 'w', encoding='utf-8') as csv:
        header = True
        for line in f:
            df = pandas.read_json(''.join(('[', line.rstrip(), ']')))
            df.to_csv(csv, header=header, index=0, quoting=1)
            header = False

我在 Mac 上使用 python 3.5 对此进行了测试;它应该可以在 Windows 上运行,但我还没有在那里测试过。

注意事项:

  1. 我已经调整了你的 json 数据;第一行的纬度/经度似乎有错误。

  2. 这只是用一个小文件测试过;我不确定从哪里获取 3.5 GB 文件。

  3. 我假设这是您朋友的一次性使用。如果这是生产代码,您需要验证“with”语句的异常处理是否正确。详情请见How can I open multiple files using "with open" in Python?

  4. 这应该是相当高效的,但同样,我不确定从哪里获取大文件。

【讨论】:

  • 查看ijson,它使流式传输 JSON 文件就像使用 Python 迭代器一样简单
  • @kevin: 问题:为什么你的to_csv() 不包含mode='a' 参数?在 with open 中调用 to_csv() 是否有什么东西可以使其自动附加?此外,您的代码运行良好——转换一个小文件需要更长的时间,但我的计算机不再死机,并且工作仍然在合理的时间内完成(应该很容易在一天结束时完成),所以我可以让它在后台运行。太感谢了。 (最后,我编辑了您的代码以在输出文件中包含 UTF-8 编码——在我这样做之前,我收到了带有外部输入数据的错误。)
  • @k.. 很高兴我能帮上忙!由于csv 已经打开并传入to_csv(),因此后者在写入后不会关闭文件。您可以通过查看源代码中的def save() 来验证;它在第 1476 行设置 close = Falsegithub.com/pandas-dev/pandas/blob/master/pandas/formats/…。好问题!
  • 有一个选项header=first 可以消除你的两行代码
猜你喜欢
  • 2018-11-13
  • 2014-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多