2017-08-15 3 views
1

私はpandasデータフレーム列の中にJSONオブジェクトを持っています。データフレームでは、JSONオブジェクトは辞書の配列を含む文字列のように見えます。配列は可変長(0を含む)でもかまいません。また、列はnullでもかまいません。私は以下のコードを書いていますが、これは私がしたいものです。列名は、2つのコンポーネントから構成されます。最初の要素は辞書のキーで、2番目の要素は辞書のキー値の部分文字列です。Pandas DataFrameの内部のJSONオブジェクト

このコードは大丈夫ですが、大きなデータフレームで実行すると非常に遅くなります。誰かがこれを行う高速な(そしておそらく簡単な)方法を提供できますか?また、感覚的/効率的/ピジョンソニックではないものが見える場合は、私が行ったことに穴を空けてください。私はまだ相対初心者です。ありがとう。

# Import libraries 
import pandas as pd 
from IPython.display import display # Used to display df's nicely in jupyter notebook. 
import json 

# Set some display options 
pd.set_option('max_colwidth',150) 

# Create the example dataframe 
print("Original df:") 
df = pd.DataFrame.from_dict({'ColA': {0: 123, 1: 234, 2: 345, 3: 456, 4: 567},\ 
'ColB': {0: '[{"key":"keyValue=1","valA":"8","valB":"18"},{"key":"keyValue=2","valA":"9","valB":"19"}]',\ 
    1: '[{"key":"keyValue=2","valA":"28","valB":"38"},{"key":"keyValue=3","valA":"29","valB":"39"}]',\ 
    2: '[{"key":"keyValue=4","valA":"48","valC":"58"}]',\ 
    3: '[]',\ 
    4: None}}) 
display(df) 

# Create a temporary dataframe to append results to, record by record 
dfTemp = pd.DataFrame() 

# Step through all rows in the dataframe 
for i in range(df.shape[0]): 
    # Check whether record is null, or doesn't contain any real data 
    if pd.notnull(df.iloc[i,df.columns.get_loc("ColB")]) and len(df.iloc[i,df.columns.get_loc("ColB")]) > 2: 
     # Convert the json structure into a dataframe, one cell at a time in the relevant column 
     x = pd.read_json(df.iloc[i,df.columns.get_loc("ColB")]) 
     # The last bit of this string (after the last =) will be used as a key for the column labels 
     x['key'] = x['key'].apply(lambda x: x.split("=")[-1]) 
     # Set this new key to be the index 
     y = x.set_index('key') 
     # Stack the rows up via a multi-level column index 
     y = y.stack().to_frame().T 
     # Flatten out the multi-level column index 
     y.columns = ['{1}_{0}'.format(*c) for c in y.columns] 
     # Give the single record the same index number as the parent dataframe (for the merge to work) 
     y.index = [df.index[i]] 
     # Append this dataframe on sequentially for each row as we go through the loop 
     dfTemp = dfTemp.append(y) 

# Merge the new dataframe back onto the original one as extra columns, with index mataching original dataframe 
df = pd.merge(df,dfTemp, how = 'left', left_index = True, right_index = True) 

print("Processed df:") 
display(df) 
+0

ほんの少しです。ループを、列挙型(for df.iloc [:、df.columns.get_loc( "ColB")]): 'for i、col_bで置き換えることができます。可読性を向上させるために、そのエントリへの参照を変更してください。 – Nyps

+0

ありがとう!確かにそれはより簡潔で読みやすいものになります。 – Michael

答えて

3

まず、パンダに関する一般的なアドバイス。 データフレームの行を繰り返し処理すると、間違っている可能性が高くなります。

:これを考慮して

、私たちは、「適用」方法(それはDFにはるかに少ないインデックス検索を意味し、これはおそらく、そもそもそれをスピードアップします)パンダを使用して、現在の手順を再作成することができます

# Check whether record is null, or doesn't contain any real data 
def do_the_thing(row): 
    if pd.notnull(row) and len(row) > 2: 
     # Convert the json structure into a dataframe, one cell at a time in the relevant column 
     x = pd.read_json(row) 
     # The last bit of this string (after the last =) will be used as a key for the column labels 
     x['key'] = x['key'].apply(lambda x: x.split("=")[-1]) 
     # Set this new key to be the index 
     y = x.set_index('key') 
     # Stack the rows up via a multi-level column index 
     y = y.stack().to_frame().T 
     # Flatten out the multi-level column index 
     y.columns = ['{1}_{0}'.format(*c) for c in y.columns] 

     #we don't need to re-index 
      # Give the single record the same index number as the parent dataframe (for the merge to work) 
      #y.index = [df.index[i]] 
     #we don't need to add to a temp df 
     # Append this dataframe on sequentially for each row as we go through the loop 
     return y.iloc[0] 
    else: 
     return pd.Series() 
df2 = df.merge(df.ColB.apply(do_the_thing), how = 'left', left_index = True, right_index = True) 

これは以前と全く同じ結果を返しますが、ロジックを変更していないことに注意してください。 applyメソッドはインデックスをソートするので、マージできます。

私はそれをスピードアップし、少し慣れているという点であなたの質問に答えてくれると思います。

しかし、あなたはこのデータ構造で何をしたいのか、そして自分が行っていることをよりよく構築する方法を検討するべきだと思います。

ColBが任意の長さの場合、任意の数の列を持つデータフレームになります。何らかの目的のためにこれらの値にアクセスするようになると、これは目的が何であれ、痛みを引き起こします。

ColBのすべてのエントリは重要ですか?あなたはちょうど最初のものを残して離れてもらえますか?特定のvalA valのインデックスを知る必要がありますか?

これはあなた自身に尋ねるべき質問です。次に、任意のものを確認することなく、必要な分析を行うための構造を決定します。

+1

包括的な回答をいただき、ありがとうございます。コードははるかに簡単で、より使いやすく、使いやすくなっています。あなたの提案を実装し、実行時間を20%短縮しました。他の提案にも感謝します。私は全体的なアプローチがあまり良くないことに同意します。 1つの可能性は、列から新しいデータフレームを作成し、新しい列に「キー」値を指定することです。そのため、各キー値に新しい列セットを追加するのではなく、新しい行セットを追加します。次回は、それをやり遂げる方法を見つけ出せるかどうか試してみます。 :-) – Michael