web-dev-qa-db-ja.com

jsonとしていくつかの列を持つpandasデータフレームを平坦化する方法は?

データベースからデータをロードするデータフレーム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文字列で同じ数の要素を持っているわけではありません。

私がする必要があるのは、idnameなどの通常の列をそのままにして、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にはいくつかの問題があるようです。これを行う正しい方法は何ですか?

21
sfactor

これは、カスタム関数を使用して json_normalize() を再度使用して、_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])
_

Image


EDIT:-@MartijnPietersのコメントによると、json文字列のデコードの推奨方法は json.loads() を使用すること。データソースがJSONであることがわかっている場合、 ast.literal_eval() を使用する場合に比べてはるかに高速です。

21
Nickil Maveli

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)

enter image description here

8
piRSquared

最も速いのは次のようです:

json_struct = json.loads(df.to_json(orient="records"))
df_flat = pf.io.json.json_normalize(json_struct)
0
staonas