繁体   English   中英

将带有索引格式的 large.json 文件读入 Pandas dataframe

[英]Read large .json file with index format into Pandas dataframe

我一直在关注这个答案,但在与它的作者讨论之后,它似乎只为orient='records'数据格式提供了一个解决方案。

这是区别:

# orient='records'
[
    {"Product":"Desktop Computer","Price":700},
    {"Product":"Tablet","Price":250},
    {"Product":"iPhone","Price":800},
    {"Product":"Laptop","Price":1200}
]

# orient='index'
{
    "0":{"Product":"Desktop Computer","Price":700},
    "1":{"Product":"Tablet","Price":250},
    "2":{"Product":"iPhone","Price":800},
    "3":{"Product":"Laptop","Price":1200}
}

我有索引格式,因为我的数据来自 SQL 数据库读入 dataframe 并且需要索引字段来指定每条记录。

我的 json 文件为 2.5 GB ,已从 dataframe 以orient='index'格式导出。

df.to_json('test.json', orient='index')

这意味着整个文件实际上是一个巨大的字符串,而不是像记录集合这样的列表:

{"0":{"Product":"Desktop Computer","Price":700},"1":{"Product":"Tablet","Price":250},"2":{"Product":"iPhone","Price":800},"3":{"Product":"Laptop","Price":1200}}

这意味着我不能像这样使用任何基于行或块的迭代解决方案:

df = pd.read_json('test.json', orient='index', lines=True, chunksize=5)

根据文档lines=True只能在记录采用类似列表的格式时使用,这就是为什么pandas.DataFrame.to_json甚至不接受这个参数,除非 orient 不是orient='records' chunksize=的限制也来自于此,它说:

"This can only be passed if lines=True. If this is None, the file will be read into memory all at once."

这正是问题的原因,试图读取如此巨大的.json 文件返回:

df = pd.read_json('test.json', orient='index')

File "C:\Users\Username\AppData\Local\Programs\Python\Python37\lib\site-
packages\pandas\io\json\_json.py", line 1100,
in _parse_no_numpy                                                                                          
loads(json, precise_float=self.precise_float),
MemoryError 

我也在考虑将索引值添加为第一列,在这种情况下,它不会因记录格式而丢失; 或者甚至可以单独存储一个索引列表。 只是我担心它会在以后降低搜索性能。

是否有任何解决方案来严格使用.json文件而不使用其他数据库或基于大数据的技术来处理这种情况?

更新#1

这里的请求是我的数据的实际结构。 SQL 表:

          Serial           Date                   PatientID     Type Gender  YearWeek
0         425571118001461E 2011-06-30 20:59:30    186092        3    1.0     2011-w26
1         425571118001461E 2011-06-30 20:55:30    186092        3    1.0     2011-w26
2         425571118001461E 2013-08-28 09:29:30    186092        3    1.0     2013-w35
3         425571118001461E 2013-08-28 07:44:30    186092        3    1.0     2013-w35
4         425571118001461E 2013-08-27 20:44:30    186092        3    1.0     2013-w35
...                    ...                 ...       ...      ...    ...         ...
32290281  4183116300254921 2020-04-09 08:07:50    217553        8    2.0     2020-w15
32290282  4183116300254921 2020-04-08 10:29:50    217553        8    2.0     2020-w15
32290283  4141119420031548 2020-04-20 10:18:02    217555       12    2.0     2020-w17
32290284  4141119420043226 2020-04-20 12:33:11    217560       12    NaN     2020-w17
32290285  4141119420000825 2020-04-20 17:31:44    217568       12    1.0     2020-w17

pandas pivot 表与示例中的表几乎相同,但有 50,000 行和 4,000 列:

df = df.pivot_table(index='PatientID', values='Serial', columns='YearWeek', aggfunc=len, fill_value=0)

YearWeek  1969-w01  1969-w02  1969-w03  1969-w04  1969-w05  ...  2138-w17  2138-w18  2138-w19  2138-w20  2138-w21
PatientID
0                0         0         0         0         0  ...         0         0         0         0         0
455              1         0         3         0         0  ...         0         0         0         0         0
40036            0         0         0         0         0  ...         0         0         0         0         0
40070            0         0         0         0         0  ...         0         0         0         0         0
40082            0         0         0         0         0  ...         0         0         0         0         0
...            ...       ...       ...       ...       ...  ...       ...       ...       ...       ...       ...
217559           0         0         0         0         0  ...         0         0         0         0         0
217560           0         0         0         0         0  ...         0         0         0         0         0
217561           0         0         0         0         0  ...         0         0         0         0         0
217563           0         0         0         0         0  ...         0         0         0         0         0
217568           0         1         0         2         0  ...         0         0         0         0         0

这就是使用格式为 json 的索引保存它的方式:

{
    "0":{"1969-w01":0,"1969-w02":0,"1969-w03":0,"1969-w04":0, ...},
    "455":{"1969-w01":1,"1969-w02":0,"1969-w03":3,"1969-w04":0, ...},
    "40036":{"1969-w01":0,"1969-w02":0,"1969-w03":0,"1969-w04":0, ...},
    ...
    "217568":{"1969-w01":0,"1969-w02":1,"1969-w03":0,"1969-w04":2, ...}
}

只有我不能给出line=True arg,所以它实际上被限制在一个巨大的字符串中,使它成为一个单线 json:

{"0":{"1969-w01":0,"1969-w02":0,"1969-w03":0,"1969-w04":0, ...},"455":{"1969-w01":1,"1969-w02":0,"1969-w03":3,"1969-w04":0, ...},"40036":{"1969-w01":0,"1969-w02":0,"1969-w03":0,"1969-w04":0, ...}, ... "217568":{"1969-w01":0,"1969-w02":1,"1969-w03":0,"1969-w04":2, ...}}

一些解决方案,从最简单到涉及更多:

1. SQL

如果您可以在数据库上执行查询,也许最好的解决方案是尝试以更好的格式写入数据? 或者,您可以尝试直接从数据库中读取 - Pandas 也可以这样做:) 这是pd.read_sql() 的文档

2. orient=...有必要吗?

要按照您给出的示例读取 JSON 文件,并创建一个与您的数据透视表示例相当的形式的 DataFrame(JSON 键为 dataframe 索引,您可以尝试以下简单方法:

# read and transpose!
df = pd.read_json("test.json").T

但是,这可能无法解决 memory 问题。

3.拆分成多个文件

也许最快的方法是简单地将大文件切割成更小的文件,每个文件都可以读取到 Pandas Dataframe (限制工作pd.concat pd.merge

Linux 中有一个很好的工具,叫做split ,它可以做到 我注意到您正在使用 windows(如果您启用它,较新的 windows 版本会提供 Linux 终端。),否则可能有类似的工具。 但我不知道我害怕。

如果您只需要这样做一次然后继续您的生活,您也许可以使用 Emacs 或 VS Code 等文本编辑器打开您的文件,然后将部分复制粘贴到新文件中......蹩脚,但可能会工作¯ \_(ツ)_/¯

4. 流式阅读器

一个名为 ijson 的ijson将迭代加载 JSON 文件,该文件允许您定义中断或对每个嵌套分区进行处理 - 例如,您可以创建 Z251D2BBFE9A3B95E5691CEB3DCrecords格式。 该解决方案还承诺低 memory 消耗,作为一个迭代器(又名生成器) - 你需要了解它是如何工作的。 看看这里有一个很好的解释

另一个名为json-streamer 的package 也可以读取部分 JSON 内容,尽管它可能有点远,因为您有一个 static 文件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM