JSON 是一種廣泛采用的基于文本的信息格式,可在系統之間互操作,最常見于 web 應用程序。雖然 JSON 格式是人類可讀的,但使用數據科學和數據工程工具處理它很復雜。
為了彌補這一差距, RAPIDS cuDF 提供了一個 GPU 加速的 JSON 讀取器( cudf.read_json ),該讀取器對于許多 JSON 數據結構都是高效和健壯的。 JSON format 指定了一種通用的樹狀數據結構, cuDF 實現了算法,可以輕松地將 JSON 樹轉換為柱狀數據。
cuDF 是一個 GPU DataFrame 庫,用于在 Python 中加載、連接、聚合、過濾和以其他方式操作數據。當 JSON 數據被構造為柱狀數據時,它可以訪問強大的 cuDF DataFrame API 。我們很高興能夠通過本讀者打開 GPU 加速到更多數據格式、項目和建模工作流的可能性。
本文重點介紹了支持的 JSON 數據選項:面向記錄的 JSON 和 JSON 行。以下是幾個 cuDF 讀取器選項的示例,用于處理具有字節范圍或多個源的 JSON 行文件。最后,您將學習如何使用 cuDF 中的工具來展平 cuDF 中的列表和結構類型,以及如何應用這些工具從常見的 JSON 模式組裝 DataFrame 。
在 cuDF 中讀取 JSON 數據
默認情況下, cuDF JSON 讀取器需要使用 records 方向的輸入數據。面向記錄的 JSON 數據由根級別的對象數組組成,數組中的每個對象對應一行。對象中的字段名決定表的列名。
JSON 數據的另一個常見變體是 JSON 行,其中 JSON 對象由換行符(n)分隔,每個對象對應一行。
以下代碼示例顯示了面向記錄的 JSON 以及 JSON 行數據:
>>> j = '''[
... {"a": "v1", "b": 12},
... {"a": "v2", "b": 7},
... {"a": "v3", "b": 5}
... ]'''
>>> df_records = cudf.read_json(j)
>>> j = 'n'.join([
... '{"a": "v1", "b": 12}',
... '{"a": "v2", "b": 7}',
... '{"a": "v3", "b": 5}'
... ])
>>> df_lines = cudf.read_json(j, lines=True)
>>> df_lines
a b
0 v1 12
1 v2 7
2 v3 5
>>> df_records.equals(df_lines)
True
cuDF JSON 讀取器還與嵌套的 JSON 對象和數組兼容,這些對象和數組大致映射到結構和列表 data types in cuDF 。
以下示例演示了用于生成列表和結構列以及數據類型為列表和結構的任意組合的列的輸入和輸出。
# example with columns types:
# list and struct
>>> j = '''[
... {"list": [0, 1, 2], "struct": {"k": "v1"}},
... {"list": [3, 4, 5], "struct": {"k": "v2"}}
... ]'''
>>> df = cudf.read_json(j)
>>> df
list struct
0 [0, 1, 2] {'k': 'v1'}
1 [3, 4, 5] {'k': 'v2'}
# example with columns types:
# list> and struct, m:int>
>>> j = 'n'.join([
... '{"a": [{"k": 0}], "b": {"k": [0, 1], "m": 5}}',
... '{"a": [{"k": 1}, {"k": 2}], "b": {"k": [2, 3], "m": 6}}',
... ])
>>> df = cudf.read_json(j, lines=True)
>>> df
a b
0 [{'k': 0}] {'k': [0, 1], 'm': 5}
1 [{'k': 1}, {'k': 2}] {'k': [2, 3], 'm': 6}
處理大小 JSON 行文件
對于基于 JSON Lines 數據的工作負載, cuDF 包括幫助數據處理的讀取器選項:大文件的字節范圍支持和小文件的多源支持。
字節范圍支持
一些工作流,如欺詐檢測和用戶行為建模,需要處理可能超過 GPU 內存容量的大型 JSON Line 文件。
cuDF 中的 JSON 讀取器支持字節范圍參數,該參數指定起始字節偏移量和字節大小。讀取器解析在字節范圍內開始的每個記錄,因此,字節范圍不必與記錄邊界對齊。
在分布式工作流中,字節范圍使每個工作人員能夠處理數據的子集。在過濾和聚合中,字節范圍允許單個工作人員以塊的形式處理數據。
為了避免跳過行或讀取重復的行,字節范圍應該相鄰,如下例所示。
>>> num_rows = 10
>>> j = 'n'.join([
... '{"id":%s, "distance": %s, "unit": "m/s"}' % x
... for x in zip(range(num_rows), cupy.random.rand(num_rows))
... ])
>>> chunk_count = 4
>>> chunk_size = len(j) // chunk_count + 1
>>> data = []
>>> for x in range(chunk_count):
... d = cudf.read_json(
... j,
... lines=True,
... byte_range=(chunk_size * x, chunk_size)
... )
... data.append(d)
>>> df = cudf.concat(data)
多源支持
相比之下,一些工作流需要處理許多小的 JSON 行文件。
cuDF 中的 JSON 讀取器接受數據源列表,而不是循環通過源并連接生成的 DataFrame 。然后將原始輸入作為單個源進行有效處理。
cuDF 中的 JSON 讀取器接受源作為文件路徑、原始字符串或類似文件的對象,以及這些源的列表。
>>> j1 = '{"id":0}n{"id":1}n'
>>> j2 = '{"id":2}n{"id":3}n'
>>> df = cudf.read_json([j1, j2], lines=True)
解包列表和結構數據
將 JSON 數據讀入帶有列表和結構列類型的 cuDF DataFrame 后,許多工作流的下一步是將數據提取或展平為簡單類型。
對于結構列,一種解決方案是使用struct.explode訪問器提取數據,并將結果連接到父 DataFrame 。
下面的代碼示例演示如何從結構列中提取數據。
>>> j = 'n'.join([
... '{"x": "Tokyo", "y": {"country": "Japan", "iso2": "JP"}}',
... '{"x": "Jakarta", "y": {"country": "Indonesia", "iso2": "ID"}}',
... '{"x": "Shanghai", "y": {"country": "China", "iso2": "CN"}}'
... ])
>>> df = cudf.read_json(j, lines=True)
>>> df = df.drop(columns='y').join(df['y'].struct.explode())
>>> df
x country iso2
0 Tokyo Japan JP
1 Jakarta Indonesia ID
2 Shanghai China CN
對于元素順序有意義的列表列,list.get訪問器從特定位置提取元素。然后,可以將生成的cudf.Series對象分配給 DataFrame 中的新列。
下面的代碼示例演示如何從列表列中提取第一個和第二個元素。
>>> j = 'n'.join([
... '{"name": "Peabody, MA", "coord": [42.53, -70.98]}',
... '{"name": "Northampton, MA", "coord": [42.32, -72.66]}',
... '{"name": "New Bedford, MA", "coord": [41.63, -70.93]}'
... ])
>>> df = cudf.read_json(j, lines=True)
>>> df['latitude'] = df['coord'].list.get(0)
>>> df['longitude'] = df['coord'].list.get(1)
>>> df = df.drop(columns='coord')
>>> df
name latitude longitude
0 Peabody, MA 42.53 -70.98
1 Northampton, MA 42.32 -72.66
2 New Bedford, MA 41.63 -70.93
最后,對于長度可變的列表列,explode方法將創建一個新的 DataFrame ,每個列表元素作為一行。將分解的 DataFrame 連接到父 DataFrame 上會產生一個具有所有簡單類型的輸出。
以下示例展平列表列,并將其連接到父 DataFrame 中的索引和其他數據。
>>> j = 'n'.join([
... '{"product": "socks", "ratings": [2, 3, 4]}',
... '{"product": "shoes", "ratings": [5, 4, 5, 3]}',
... '{"product": "shirts", "ratings": [3, 4]}'
... ])
>>> df = cudf.read_json(j, lines=True)
>>> df = df.drop(columns='ratings').join(df['ratings'].explode())
>>> df
product ratings
0 socks 2
0 socks 4
0 socks 3
1 shoes 5
1 shoes 5
1 shoes 4
1 shoes 3
2 shirts 3
2 shirts 4
使用 cuDF 構建 JSON 數據解決方案
有時,工作流必須使用對象根處理 JSON 數據。 cuDF 提供了為此類數據構建解決方案的工具。要使用對象根處理 JSON 數據,我們建議將數據作為單個 JSON 行讀取,然后拆包生成的 DataFrame 。
以下示例將 JSON 對象作為單行讀取,然后將“ results ”字段提取到新的 DataFrame 中。
>>> j = '''{
... "metadata" : {"vehicle":"car"},
... "results": [
... {"id": 0, "distance": 1.2},
... {"id": 1, "distance": 2.4},
... {"id": 2, "distance": 1.7}
... ]
... }'''
# first read the JSON object with lines=True
>>> df = cudf.read_json(j, lines=True)
>>> df
metadata records
0 {'vehicle': 'car'} [{'id': 0, 'distance': 1.2}, {'id': 1, 'distan...
# then explode the 'records' column
>>> df = df['records'].explode().struct.explode()
>>> df
id distance
0 0 1.2
1 1 2.4
2 2 1.7
關鍵要點
cuDF JSON 讀取器旨在加速廣泛的 JSON 數據工作負載,包括跨大文件和小文件的簡單和復雜類型。
這篇文章演示了 cuDF JSON 讀取器與面向記錄和 JSON 行數據的常見用法,以及展示字節范圍和多源支持。現在,您可以加快處理 JSON 數據的方式,并將 JSON 數據有效地結合到工作流中。
-
NVIDIA
+關注
關注
14文章
5592瀏覽量
109719 -
gpu
+關注
關注
28文章
5194瀏覽量
135429 -
AI
+關注
關注
91文章
39755瀏覽量
301359
發布評論請先 登錄
JSON 數據格式
怎么支持PIC32中的JSON解析和序列化
android使用JSON進行網絡數據交換
PHP如何返回json格式的數據給jquery的詳細資料說明
支持的JSON數據選項介紹
評論