Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pandas - expand nested json array within column in dataframe

I have a json data (coming from mongodb) containing thousands of records (so an array/list of json object) with a structure like the below one for each object:

{
   "id":1,
   "first_name":"Mead",
   "last_name":"Lantaph",
   "email":"[email protected]",
   "gender":"Male",
   "ip_address":"231.126.209.31",
   "nested_array_to_expand":[
      {
         "property":"Quaxo",
         "json_obj":{
            "prop1":"Chevrolet",
            "prop2":"Mercy Streets"
         }
      },
      {
         "property":"Blogpad",
         "json_obj":{
            "prop1":"Hyundai",
            "prop2":"Flashback"
         }
      },
      {
         "property":"Yabox",
         "json_obj":{
            "prop1":"Nissan",
            "prop2":"Welcome Mr. Marshall (Bienvenido Mister Marshall)"
         }
      }
   ]
}

When loaded in a dataframe the "nested_array_to_expand" is a string containing the json (I do use "json_normalize" during loading). The expected result is to get a dataframe with 3 row (given the above example) and new columns for the nested objects such as below:

index   email first_name gender  id      ip_address last_name  \
0  [email protected]       Mead   Male   1  231.126.209.31   Lantaph   
1  [email protected]       Mead   Male   1  231.126.209.31   Lantaph   
2  [email protected]       Mead   Male   1  231.126.209.31   Lantaph   

  test.name                                      test.obj.ahah test.obj.buzz  
0     Quaxo                                      Mercy Streets     Chevrolet  
1   Blogpad                                          Flashback       Hyundai  
2     Yabox  Welcome Mr. Marshall (Bienvenido Mister Marshall)        Nissan  

I was able to get that result with the below function but it extremely slow (around 2s for 1k records) so I would like to either improve the existing code or find a completely different approach to get this result.

def expand_field(field, df, parent_id='id'):
    all_sub = pd.DataFrame()
    # we need an id per row to be able to merge back dataframes
    # if no id, then we will create one based on index of rows
    if parent_id not in df:
        df[parent_id] = df.index

    # go through all rows and create a new dataframe with values
    for i, row in df.iterrows():
        try:
            sub = json_normalize(df[field].values[i])
            sub = sub.add_prefix(field + '.')
            sub['parent_id'] = row[parent_id]
            all_sub = all_sub.append(sub)
        except:
            print('crash')
            pass
    df = pd.merge(df, all_sub, left_on=parent_id, right_on='parent_id', how='left')
    #remove old columns
    del df["parent_id"]
    del df[field]
    #return expanded dataframe
    return df

Many thanks for your help.

===== EDIT for answering comment ====

The data loaded from mongodb is an array of object. I load it with the following code:

data = json.loads(my_json_string)
df = json_normalize(data)

The output give me a dataframe with df["nested_array_to_expand"] as dtype object (string)

0    [{'property': 'Quaxo', 'json_obj': {'prop1': '...
Name: nested_array_to_expand, dtype: object
like image 599
Eric D. Avatar asked Dec 12 '17 04:12

Eric D.


People also ask

How do I flatten nested JSON in a data frame?

Pandas have a nice inbuilt function called json_normalize() to flatten the simple to moderately semi-structured nested JSON structures to flat tables. Parameters: data – dict or list of dicts. errors – {'raise', 'ignore'}, default 'raise'

How do you access nested elements in a JSON object?

Accessing nested json objects is just like accessing nested arrays. Nested objects are the objects that are inside an another object. In the following example 'vehicles' is a object which is inside a main object called 'person'. Using dot notation the nested objects' property(car) is accessed.

How do I read a nested JSON file in Python?

Python has built in functions that easily imports JSON files as a Python dictionary or a Pandas dataframe. Use pd. read_json() to load simple JSONs and pd. json_normalize() to load nested JSONs.

How do you convert nested JSON to DataFrame in Pyspark?

Convert to DataFrameAdd the JSON string as a collection type and pass it as an input to spark. createDataset. This converts it to a DataFrame. The JSON reader infers the schema automatically from the JSON string.


1 Answers

I propose an interesting answer I think using pandas.json_normalize.
I use it to expand the nested json -- maybe there is a better way, but you definitively should consider using this feature. Then you have just to rename the columns as you want.

import io
from pandas import json_normalize

# Loading the json string into a structure
json_dict = json.load(io.StringIO(json_str))

df = pd.concat([pd.DataFrame(json_dict), 
                json_normalize(json_dict['nested_array_to_expand'])], 
                axis=1).drop('nested_array_to_expand', 1)

enter image description here

like image 57
Romain Avatar answered Sep 21 '22 15:09

Romain