วิธี json_normalize คอลัมน์ด้วย NaNs

Sep 13 2020
  • คำถามนี้เฉพาะสำหรับคอลัมน์ของข้อมูลในไฟล์ pandas.DataFrame
  • คำถามนี้ขึ้นอยู่กับว่าค่าในคอลัมน์ที่มีstr, dictหรือlistประเภท
  • คำถามนี้จัดการกับNaNค่าเมื่อdf.dropna().reset_index(drop=True)ไม่ใช่ตัวเลือกที่ถูกต้อง

กรณีที่ 1

  • คอลัมน์ของstrประเภทค่าในคอลัมน์ต้องถูกแปลงเป็นdictชนิดที่มีก่อนที่จะใช้ast.literal_eval.json_normalize
import numpy as np
import pandas as pd
from ast import literal_eval

df = pd.DataFrame({'col_str': ['{"a": "46", "b": "3", "c": "12"}', '{"b": "2", "c": "7"}', '{"c": "11"}', np.NaN]})

                            col_str
0  {"a": "46", "b": "3", "c": "12"}
1              {"b": "2", "c": "7"}
2                       {"c": "11"}
3                               NaN

type(df.iloc[0, 0])
[out]: str

df.col_str.apply(literal_eval)

ข้อผิดพลาด:

df.col_str.apply(literal_eval) results in ValueError: malformed node or string: nan

กรณีที่ 2

  • ด้วยประเภทคอลัมน์dictให้ใช้pandas.json_normalizeเพื่อแปลงคีย์เป็นส่วนหัวคอลัมน์และค่าเป็นแถว
df = pd.DataFrame({'col_dict': [{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}, {"c": "11"}, np.NaN]})

                           col_dict
0  {'a': '46', 'b': '3', 'c': '12'}
1              {'b': '2', 'c': '7'}
2                       {'c': '11'}
3                               NaN

type(df.iloc[0, 0])
[out]: dict

pd.json_normalize(df.col_dict)

ข้อผิดพลาด:

pd.json_normalize(df.col_dict) results in AttributeError: 'float' object has no attribute 'items'

กรณีที่ 3

  • ในคอลัมน์strประเภทโดยdictอยู่ภายใน a list.
  • เพื่อทำให้คอลัมน์เป็นปกติ
    • นำไปใช้literal_evalเนื่องจากระเบิดไม่ทำงานในstrประเภท
    • ระเบิดคอลัมน์dictsเพื่อแยกแถวเพื่อแยกแถว
    • ทำให้คอลัมน์เป็นปกติ
df = pd.DataFrame({'col_str': ['[{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}]', '[{"b": "2", "c": "7"}, {"c": "11"}]', np.nan]})

                                                    col_str
0  [{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}]
1                       [{"b": "2", "c": "7"}, {"c": "11"}]
2                                                       NaN

type(df.iloc[0, 0])
[out]: str
    
df.col_str.apply(literal_eval)

ข้อผิดพลาด:

df.col_str.apply(literal_eval) results in ValueError: malformed node or string: nan

คำตอบ

2 TrentonMcKinney Sep 13 2020 at 23:59
  • ตามที่ระบุไว้ในความคิดเห็นมีตัวเลือกให้:
    • df = df.dropna().reset_index(drop=True)
    • ใช้ได้ดีสำหรับข้อมูลจำลองที่นี่หรือเมื่อจัดการกับดาต้าเฟรมที่คอลัมน์อื่น ๆ ไม่สำคัญ
    • ไม่ใช่ตัวเลือกที่ดีสำหรับดาต้าเฟรมที่มีคอลัมน์เพิ่มเติมที่จำเป็น

กรณีที่ 1

  • เนื่องจากคอลัมน์มีstrประเภทจึงเติมด้วย'{}'(a str)
import numpy as np
import pandas as pd
from ast import literal_eval

df = pd.DataFrame({'col_str': ['{"a": "46", "b": "3", "c": "12"}', '{"b": "2", "c": "7"}', '{"c": "11"}', np.NaN]})

                            col_str
0  {"a": "46", "b": "3", "c": "12"}
1              {"b": "2", "c": "7"}
2                       {"c": "11"}
3                               NaN

type(df.iloc[0, 0])
[out]: str

# fillna
df.col_str = df.col_str.fillna('{}')

# convert the column to dicts
df.col_str = df.col_str.apply(literal_eval)

# use json_normalize
df = df.join(pd.json_normalize(df.col_str)).drop(columns=['col_str'])

# display(df)
     a    b    c
0   46    3   12
1  NaN    2    7
2  NaN  NaN   11
3  NaN  NaN  NaN

กรณีที่ 2

  • เนื่องจากคอลัมน์มีdictประเภทจึงเติมด้วย{}(ไม่ใช่ a str)
  • สิ่งนี้จำเป็นต้องกรอกโดยใช้การเข้าใจตามคำบอกเนื่องจากfillna({})ไม่ได้ผล
df = pd.DataFrame({'col_dict': [{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}, {"c": "11"}, np.NaN]})

                           col_dict
0  {'a': '46', 'b': '3', 'c': '12'}
1              {'b': '2', 'c': '7'}
2                       {'c': '11'}
3                               NaN

type(df.iloc[0, 0])
[out]: dict
    
# fillna
df.col_dict = df.col_dict.fillna({i: {} for i in df.index})

# use json_normalize
df = df.join(pd.json_normalize(df.col_dict)).drop(columns=['col_dict'])

# display(df)
     a    b    c
0   46    3   12
1  NaN    2    7
2  NaN  NaN   11
3  NaN  NaN  NaN

กรณีที่ 3

  1. เติมNaNsด้วย'[]'(a str)
  2. ตอนนี้literal_evalจะทำงาน
  3. .explodeสามารถใช้ในคอลัมน์เพื่อแยกdictค่าออกเป็นแถว
  4. ตอนนี้NaNsจำเป็นต้องเติมเต็มด้วย{}(ไม่ใช่str)
  5. จากนั้นคอลัมน์สามารถทำให้เป็นมาตรฐานได้
  • สำหรับกรณีที่คอลัมน์ที่เป็นlistsของdictsที่ไม่ได้จัดพิมพ์ให้ข้ามไปstr.explode
df = pd.DataFrame({'col_str': ['[{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}]', '[{"b": "2", "c": "7"}, {"c": "11"}]', np.nan]})

                                                    col_str
0  [{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}]
1                       [{"b": "2", "c": "7"}, {"c": "11"}]
2                                                       NaN

type(df.iloc[0, 0])
[out]: str
    
# fillna
df.col_str = df.col_str.fillna('[]')

# literal_eval
df.col_str = df.col_str.apply(literal_eval)

# explode
df = df.explode('col_str').reset_index(drop=True)

# fillna again
df.col_str = df.col_str.fillna({i: {} for i in df.index})

# use json_normalize
df = df.join(pd.json_normalize(df.col_str)).drop(columns=['col_str'])

# display(df)
     a    b    c
0   46    3   12
1  NaN    2    7
2  NaN    2    7
3  NaN  NaN   11
4  NaN  NaN  NaN