Cách json_normalize một cột bằng NaNs

Sep 13 2020
  • Câu hỏi này dành riêng cho các cột dữ liệu trong pandas.DataFrame
  • Câu hỏi này phụ thuộc vào nếu các giá trị trong cột str, dicthoặc listloại.
  • Câu hỏi này đề cập đến việc xử lý các NaNgiá trị, khi nào df.dropna().reset_index(drop=True)không phải là một tùy chọn hợp lệ.

Trường hợp 1

  • Với một cột strkiểu, các giá trị trong cột phải được chuyển đổi thành dictkiểu, với ast.literal_eval, trước khi sử dụng .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)

Lỗi:

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

Trường hợp 2

  • Với loại cột dict, hãy sử dụng pandas.json_normalizeđể chuyển đổi khóa thành tiêu đề cột và giá trị thành hàng
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)

Lỗi:

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

Trường hợp 3

  • Trong một cột strloại, với dictbên trong a list.
  • Để chuẩn hóa cột
    • áp dụng literal_eval, vì phát nổ không hoạt động trên strloại
    • nổ cột để tách các dictshàng riêng biệt
    • bình thường hóa cột
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)

Lỗi:

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

Trả lời

2 TrentonMcKinney Sep 13 2020 at 23:59
  • Như đã chỉ ra trong một nhận xét, luôn có tùy chọn để:
    • df = df.dropna().reset_index(drop=True)
    • Điều đó tốt cho dữ liệu giả ở đây hoặc khi xử lý khung dữ liệu trong đó các cột khác không quan trọng.
    • Không phải là một lựa chọn tuyệt vời cho khung dữ liệu với các cột bổ sung được yêu cầu.

Trường hợp 1

  • Vì cột chứa strcác loại, điền vào bằng '{}'(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

Trường hợp 2

  • Vì cột chứa dictcác loại, nên điền vào {}(không phải a str)
  • Điều này cần được điền bằng cách sử dụng đọc hiểu chính tả, vì fillna({})nó không hoạt động
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

Trường hợp 3

  1. Điền vào NaNsvới '[]'(a str)
  2. Bây giờ literal_evalsẽ hoạt động
  3. .explodecó thể được sử dụng trên cột để tách các dictgiá trị thành các hàng
  4. Bây giờ NaNsnhu cầu được lấp đầy bằng {}(không phải a str)
  5. Sau đó, cột có thể được chuẩn hóa
  • Đối với trường hợp cột là listscủa dicts, không strthuộc loại, hãy bỏ qua .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