꿈을 향해 on my way

Pandas - json_normalize 문제 총 정리 본문

데이터 사이언스 공부

Pandas - json_normalize 문제 총 정리

박재성 2022. 4. 2. 02:06

1. KeyError

: pandas 'json_normalize' 를 쓰다 보면 KeyError가 자주 뜬다. argument 없이 간단한 json 파일을 처리할 땐 문제가 없는데 'record_path' 랑 'meta' argument 를 쓰다 보면 KeyError 가 자주 발생한다. json 파일 형태가 일률적이지 않을 때가 특히 그런데 예를 들어,

response = {'contacts': [{'contact_type': 'individual',
   'is_green_match': True,
   'is_signatory': True,
   'match_score': 101,
   'persons': [{'addresses': [{'city': 'NEW YORK',
       'line1': '31 W 34TH ST',
       'postal_code': '10001',
       'state_code': 'NY'}],
     'display': 'Ishay Oved',
     'emails': [],
     'first_name': 'Ishay',
     'id': '8e6d391d-0fa8-5e9d-9880-fd1069c4d190',
     'jobs': [],
     'last_name': 'Oved',
     'phones': [],
     'urls': []}]},
  {'company': {'addresses': [{'city': 'NEW YORK',
      'country_code': 'USA',
      'line1': '1185 6TH AVE FL 10',
      'postal_code': '10036',
      'state_code': 'NY'}],
    'emails': [],
    'id': '2987b8f5-9d94-5858-b065-1fc62b315e80',
    'match_score': 53,
    'name': '31 WEST 34TH STREET LLC',
    'phones': [],
    'urls': []},
   'contact_type': 'company',
   'is_green_match': True,
   'is_signatory': False,
   'match_score': 53,
   'persons': []}],
 'owner_update_time': '2021-08-26',
 'property_id': '2c1820dc-2a57-5532-8022-0a8840e32da7'}

 

df_test = pd.json_normalize(
        response,
        record_path=['contacts', 'company', 'addresses'],
        meta = ['property_id', 
        ['contacts', 'company', 'id'],
        ['contacts', 'company', 'name'],
        ['contacts', 'company', 'match_score'],
        ['contacts', 'company', 'employees_total'],
        ['contacts', 'company', 'sales_volume'],
        ['contacts', 'company', 'urls'],
        ['contacts', 'company', 'year_founded'],
        ['contacts', 'company', 'dba_name'],
        ['contacts', 'company', 'parent_id']],
        record_prefix='company.addresses.',
        errors='ignore'
        )

를 실행하면, KeyError가 뜬다. 

오류가 뜨는 이유는 리스트내 딕셔너리에 해당 키 ('company') 가 없기 때문이다.

 

 

해결법은 간단하다. 리스트 안 모든 딕셔너리에 내가 파싱하고자 하는 키 (위의 예에선 'company') 를 심어주는 것. 

for i in response['contacts']:
	if 'company' not in i.keys():
    	i.update({'company':[{'addresses':None,'emails':None, 'urls':None}]})

위 코드를 실행하면, 없던 'company' key가 생기고 KeyError 가 사라진다. 

 

여기서 혼란스러운게, KeyError를 방지하기 위해 'errors = 'ignore'' 했는데 왜 KeyError가 또 나오지? 알아야 할 사실이, error argument 는 'meta' 에 적용되고 'record_path' 에는 적용되지 않는다는 점. 따라서, 'record_path' 에서 KeyError 가 발생하면, 'error' argument 로 해결이 안되고 위 해결방법처럼 리스트 안 모든 데이터에 해당 empty value로 해당 key를 만들어주어야 한다.

 

2. groupby -> convert rows into multiple columns

: 'record_type' 으로 데이터를 쪼개다 보면 하나의 row에 있었으면 하는 데이터가 여러 개의 rows 로 배치되어 있는 경우가 있다. 무슨말인가 하면, 

 

현 상태

data = {'groupId':[1,1,2], 'email':['a1@gmail.com', 'a2@gmail.com', 'a3@gmail.com'],
        'type':['office','personal','personal'],'name':['santy','santy','will']} 
df = pd.DataFrame(data)
groupId email   type           name
1   a1@gmail.com    office      santy
1   a2@gmail.com    personal    santy
2   a3@gmail.com    personal    will

 

 

이상적인 상태 (groupId 로 그룹핑)

groupId email1         type1   email2          type2       name
1      a1@gmail.com  office    a2@gmail.com    personal    santy
2      a3@gmail.com   personal   na              na        will

 

 

해결법 ->  pandas groupby 를 활용.

new_df = (df.assign(col=df.groupby('groupId').cumcount()+1)
   .set_index(['groupId','col'])
   .unstack('col')
   .sort_index(level=(1,0), axis=1)
)

new_df.columns = [f'{x}{y}' for x,y in new_df.columns]
               email1     type1        email2     type2
groupId                                                
1        a1@gmail.com    office  a2@gmail.com  personal
2        a3@gmail.com  personal           NaN       NaN

 

Comments