programing

json이라고 하는 컬럼이 있는 판다의 데이터 프레임을 평평하게 하려면 어떻게 해야 합니까?

css3 2023. 3. 31. 22:35

json이라고 하는 컬럼이 있는 판다의 데이터 프레임을 평평하게 하려면 어떻게 해야 합니까?

데이터 프레임이 있습니다.df데이터베이스에서 데이터를 로드합니다.대부분의 열은 json 문자열이고 일부는 json 목록입니다.예를 들어 다음과 같습니다.

id     name     columnA                               columnB
1     John     {"dist": "600", "time": "0:12.10"}    [{"pos": "1st", "value": "500"},{"pos": "2nd", "value": "300"},{"pos": "3rd", "value": "200"}, {"pos": "total", "value": "1000"}]
2     Mike     {"dist": "600"}                       [{"pos": "1st", "value": "500"},{"pos": "2nd", "value": "300"},{"pos": "total", "value": "800"}]
...

보시는 것처럼 모든 행이 열의 json 문자열에서 동일한 수의 요소가 있는 것은 아닙니다.

제가 해야 할 일은 정상 컬럼을 다음과 같이 유지하는 것입니다.id그리고.name다음과 같이 json 열을 평평하게 합니다.

id    name   columnA.dist   columnA.time   columnB.pos.1st   columnB.pos.2nd   columnB.pos.3rd     columnB.pos.total
1     John   600            0:12.10        500               300               200                 1000 
2     Mark   600            NaN            500               300               Nan                 800 

사용해보았습니다.json_normalize다음과 같이 합니다.

from pandas.io.json import json_normalize
json_normalize(df)

하지만 에 몇 가지 문제가 있는 것 같습니다.keyerror올바른 방법은 무엇입니까?

커스텀 함수를 사용하여 데이터를 올바른 형식으로 취득함으로써 다시 사용하는 솔루션을 다음에 나타냅니다.json_normalize기능.

import ast
from pandas.io.json import json_normalize

def only_dict(d):
    '''
    Convert json string representation of dictionary to a python dict
    '''
    return ast.literal_eval(d)

def list_of_dicts(ld):
    '''
    Create a mapping of the tuples formed after 
    converting json strings of list to a python list   
    '''
    return dict([(list(d.values())[1], list(d.values())[0]) for d in ast.literal_eval(ld)])

A = json_normalize(df['columnA'].apply(only_dict).tolist()).add_prefix('columnA.')
B = json_normalize(df['columnB'].apply(list_of_dicts).tolist()).add_prefix('columnB.pos.') 

마지막으로,DFs공통 인덱스에서 다음 정보를 얻습니다.

df[['id', 'name']].join([A, B])

이미지


편집:- @MartijnPieters의 코멘트에 의하면, 데이터 소스가 JSON인 것을 알고 있는 경우에 비해 json 문자열을 디코딩하는 것이 권장됩니다.

가장 빠른 것은 다음과 같습니다.

import pandas as pd
import json

json_struct = json.loads(df.to_json(orient="records"))    
df_flat = pd.io.json.json_normalize(json_struct) #use pd.io.json

TL;DR 다음 기능을 복사 붙여넣고 다음과 같이 사용합니다.flatten_nested_json_df(df)

이것이 제가 생각할 수 있는 가장 일반적인 기능입니다.

def flatten_nested_json_df(df):

    df = df.reset_index()

    print(f"original shape: {df.shape}")
    print(f"original columns: {df.columns}")


    # search for columns to explode/flatten
    s = (df.applymap(type) == list).all()
    list_columns = s[s].index.tolist()

    s = (df.applymap(type) == dict).all()
    dict_columns = s[s].index.tolist()

    print(f"lists: {list_columns}, dicts: {dict_columns}")
    while len(list_columns) > 0 or len(dict_columns) > 0:
        new_columns = []

        for col in dict_columns:
            print(f"flattening: {col}")
            # explode dictionaries horizontally, adding new columns
            horiz_exploded = pd.json_normalize(df[col]).add_prefix(f'{col}.')
            horiz_exploded.index = df.index
            df = pd.concat([df, horiz_exploded], axis=1).drop(columns=[col])
            new_columns.extend(horiz_exploded.columns) # inplace

        for col in list_columns:
            print(f"exploding: {col}")
            # explode lists vertically, adding new columns
            df = df.drop(columns=[col]).join(df[col].explode().to_frame())
            new_columns.append(col)

        # check if there are still dict o list fields to flatten
        s = (df[new_columns].applymap(type) == list).all()
        list_columns = s[s].index.tolist()

        s = (df[new_columns].applymap(type) == dict).all()
        dict_columns = s[s].index.tolist()

        print(f"lists: {list_columns}, dicts: {dict_columns}")

    print(f"final shape: {df.shape}")
    print(f"final columns: {df.columns}")
    return df

열에 중첩된 목록 및/또는 딕트가 있을 수 있는 데이터 프레임을 가져와서 해당 열을 반복적으로 확장/확대합니다.

팬더'는 사전을 폭발시키기 위해, 팬더는 목록을 폭발시키기 위해 사용합니다.

심플한 사용법

# Test
df = pd.DataFrame(
    columns=['id','name','columnA','columnB'],
    data=[
        [1,'John',{"dist": "600", "time": "0:12.10"},[{"pos": "1st", "value": "500"},{"pos": "2nd", "value": "300"},{"pos": "3rd", "value": "200"}, {"pos": "total", "value": "1000"}]],
        [2,'Mike',{"dist": "600"},[{"pos": "1st", "value": "500"},{"pos": "2nd", "value": "300"},{"pos": "total", "value": "800"}]]
    ])

flatten_nested_json_df(df)

가장 효율적인 방법은 아니며 데이터 프레임의 인덱스를 재설정하는 부작용도 있지만 작업을 수행할 수 있습니다.얼마든지 조정해 주세요.

평평하게 하기 위한 커스텀 함수를 작성하다columnB그 후pd.concat

def flatten(js):
    return pd.DataFrame(js).set_index('pos').squeeze()

pd.concat([df.drop(['columnA', 'columnB'], axis=1),
           df.columnA.apply(pd.Series),
           df.columnB.apply(flatten)], axis=1)

여기에 이미지 설명 입력

언급URL : https://stackoverflow.com/questions/39899005/how-to-flatten-a-pandas-dataframe-with-some-columns-as-json