본문 바로가기
python/주식

전자공시 Open API 활용(6), pandas 기초 - 재무제표 json 데이터를 pandas DataFrame으로 변환

by 맑은안개 2020. 3. 29.

Pandas는 Python으로 제공하는 외부라이브러리이다. 

데이터를 읽는 것 부터 변환, 가공하여 쓰는데까지 많은 편의성을 제공한다. 데이터를 핸들링 하는 모든 과정은 빠르고 유연하다. 또한 많은 유틸리티 함수를 제공함으로써 데이터 조작을 손쉽게 할 수 있다.

이전 블로그에서 다트 Open API를 사용하여 기업 재무제표 데이터를 json형태로 결과를 수신했다.  해당 데이터를 행,열 데이터 구조로 가공하기 위해서 Pandas의 Dataframe으로 가공해보도록 한다. 

참조: 공식 레퍼런스 사이트

 

관련 포스트 시리즈

Pandas library 설치

pip install pandas

 

Pandas DataFrame이란 ? 

Pandas 활용에 앞서 DataFrame은 무엇인지 공식레퍼런스 사이트에서 제공하는 이미지를 확인해보자.

출처: Pandas 공식 레퍼런스 사이트

위에서 보는바와 같이 dataframe은 행(row)과 열(column)로 구성된 2차원 데이터 구조를 갖는 자료구조(Data Structure)다. 엑셀과 SQL에서와 같이 데이터를 표현하는데 이상적인 구조이다. 

 

Pandas는 DataFrame 데이터구조 외에 1차원 데이터 구조의 Series를 제공한다. 

출처: Pandas 공식 레퍼런스 사이트

Pandas는 다른 데이터 포맷과의 호환성을 위해 다양한 API를 제공하는데  JSON, CSV, XLS, HTML, SQL, PARQUET등 여러 포맷을 읽고 쓸수있다.

출처: Pandas 공식 레퍼런스 사이트

개략적인 이해만 하고 코드실습으로 Pandas를 파헤쳐보자!! 

 

JSON 데이터를 DataFrame으로 변환

전 블로그의 코드를 아래와 같이 수정하였다.(requests 인스톨 선행 할 것 : pip install requests)

import requests # urllib에서 requests로 대체 함.
import json
import pandas as pd

URL = 'https://opendart.fss.or.kr/api/fnlttSinglAcnt.json'
PARAMS = {
  'crtfc_key': 'Insert your crtfc_key ', # API 인증키
  'corp_code': '00126380', # 삼성전자 고유번호
  'bsns_year': '2018', # 사업연도(4자리)
  'reprt_code': '11011', # 사업보고서
}

resp = requests.get(url = URL, params = PARAMS)

if resp.status_code == 200:
  data_json = resp.json()

  # OUTPUT
  # data_str = json.dumps(data_json, indent=4, ensure_ascii=False)
  # print(data_str)

  if data_json['status'] == "000":
    detail = data_json['list']

    # for x in detail:
      # if x['fs_div'] == 'CFS' and x['sj_div'] == 'IS' and x['account_nm'] == '당기순이익':
      # print(json.dumps(x, indent=4, ensure_ascii=False))
    
    # Json 코드 DataFrame으로 변환
    df = pd.json_normalize(detail)
    print(df)
  else :
    print(data_json['message'])

json 형태로 구조화 되어있는 데이터json_normalize 함수를 사용하여 dataframe으로 변환하였다. 

 

OUTPUT

          rcept_no reprt_code bsns_year corp_code stock_code fs_div  ...                frmtrm_dt        frmtrm_amount bfefrmtrm_nm             bfefrmtrm_dt     bfefrmtrm_amount ord
0   20190401004781      11011      2018  00126380     005930    CFS  ...            2017.12.31 현재  146,982,464,000,000       제 48 기            2016.12.31 현재  141,429,704,000,000  1
1   20190401004781      11011      2018  00126380     005930    CFS  ...            2017.12.31 현재  154,769,626,000,000       제 48 기            2016.12.31 현재  120,744,620,000,000  3
2   20190401004781      11011      2018  00126380     005930    CFS  ...            2017.12.31 현재  301,752,090,000,000       제 48 기            2016.12.31 현재  262,174,324,000,000  5
3   20190401004781      11011      2018  00126380     005930    CFS  ...            2017.12.31 현재   67,175,114,000,000       제 48 기            2016.12.31 현재   54,704,095,000,000  7
4   20190401004781      11011      2018  00126380     005930    CFS  ...            2017.12.31 현재   20,085,548,000,000       제 48 기            2016.12.31 현재   14,507,196,000,000  9
... 중략 ...
20  20190401004781      11011      2018  00126380     005930    OFS  ...            2017.12.31 현재  150,928,724,000,000       제 48 기            2016.12.31 현재  140,747,574,000,000  19
21  20190401004781      11011      2018  00126380     005930    OFS  ...            2017.12.31 현재  151,569,775,000,000       제 48 기            2016.12.31 현재  137,546,762,000,000  22
22  20190401004781      11011      2018  00126380     005930    OFS  ...  2017.01.01 ~ 2017.12.31  161,915,007,000,000       제 48 기  2016.01.01 ~ 2016.12.31  133,947,204,000,000  24
23  20190401004781      11011      2018  00126380     005930    OFS  ...  2017.01.01 ~ 2017.12.31   34,857,091,000,000       제 48 기  2016.01.01 ~ 2016.12.31   13,647,436,000,000  26
24  20190401004781      11011      2018  00126380     005930    OFS  ...  2017.01.01 ~ 2017.12.31   36,533,552,000,000       제 48 기  2016.01.01 ~ 2016.12.31   14,725,074,000,000  28
25  20190401004781      11011      2018  00126380     005930    OFS  ...  2017.01.01 ~ 2017.12.31   28,800,837,000,000       제 48 기  2016.01.01 ~ 2016.12.31   11,579,749,000,000  30

행,열 구조의 DataFrame을 확인할 수 있다.  DataFrame의 Schema는 다음과 같이 확인 할 수있다.

print(df.info(verbose=True))

 

Data columns (total 20 columns):
 #   Column            Non-Null Count  Dtype
---  ------            --------------  -----
 0   rcept_no          26 non-null     object
 1   reprt_code        26 non-null     object
 2   bsns_year         26 non-null     object
 3   corp_code         26 non-null     object
 4   stock_code        26 non-null     object
 5   fs_div            26 non-null     object
 6   fs_nm             26 non-null     object
 7   sj_div            26 non-null     object
 8   sj_nm             26 non-null     object
 9   account_nm        26 non-null     object
 10  thstrm_nm         26 non-null     object
 11  thstrm_dt         26 non-null     object
 12  thstrm_amount     26 non-null     object
 13  frmtrm_nm         26 non-null     object
 14  frmtrm_dt         26 non-null     object
 15  frmtrm_amount     26 non-null     object
 16  bfefrmtrm_nm      26 non-null     object
 17  bfefrmtrm_dt      26 non-null     object
 18  bfefrmtrm_amount  26 non-null     object
 19  ord               26 non-null     object
dtypes: object(20)
memory usage: 2.1+ KB
None

 

Selection

특정 컬럼 한개를 추출하면 Series가 반환된다. Series는 위에서 언급한것 처럼 1차원 데이터 구조를 갖는다.

    # 특정컬럼 추출
    _account_nm_sr = df['account_nm']
    print('_account_nm_sr: ', type(_account_nm_sr))
    print(_account_nm_sr)
_account_nm_sr:  <class 'pandas.core.series.Series'>
0           유동자산
1          비유동자산
2           자산총계
... 중략 ...
23          영업이익
24    법인세차감전 순이익
25         당기순이익
Name: account_nm, dtype: object

N개의 컬럼을 추출하면 DataFrame으로 반환된다.

    # N개 컬럼 추출
    _main_columns = df[['account_nm', 'fs_div', 'sj_div']]
    print('_main_columns: ', type(_main_columns))
    print(_main_columns)
_main_columns:  <class 'pandas.core.frame.DataFrame'>
    account_nm fs_div sj_div
0         유동자산    CFS     BS
1        비유동자산    CFS     BS
2         자산총계    CFS     BS
... 중략 ...
23        영업이익    OFS     IS
24  법인세차감전 순이익    OFS     IS
25       당기순이익    OFS     IS

 

Boolean indexing

컬럼 중 연결재무제표의 손익계산서 매출액, 영업이익, 법인세차감전 순이익, 당기순이익 데이터를 추출해보자. Row는 계정과목별로 나뉘어있다. 

연결재무제표와 손익계산서 데이터는 다음과 같이 정의되어있다.

 

단일회사 주요계정 개발가이드 - 응답결과 일부

조건식은 아래와 같이 두가지 방식으로 처리할 수 있다.

    # 1. 조건을 Series로 선언한 후 조립 
    _fs = df['fs_div'] == 'CFS'
    _sj = df['sj_div'] == 'IS'
    result_01 = df[_fs & _sj]

    # 2. 내부에 바로 선언, 각 조건은 ()을 묶어줘야한다.
    result_02 = df[
      (df['fs_div'] == 'CFS')
    & (df['sj_div'] == 'IS')
    ]

 

OUTPUT

          rcept_no reprt_code bsns_year corp_code stock_code fs_div   fs_nm  ... frmtrm_nm                frmtrm_dt        frmtrm_amount bfefrmtrm_nm             bfefrmtrm_dt     bfefrmtrm_amount ord
9   20190401004781      11011      2018  00126380     005930    CFS  연결재무제표  ...    제 49 기  2017.01.01 ~ 2017.12.31  239,575,376,000,000       제 48 기  2016.01.01 ~ 2016.12.31  201,866,745,000,000  23
10  20190401004781      11011      2018  00126380     005930    CFS  연결재무제표  ...    제 49 기  2017.01.01 ~ 2017.12.31   53,645,038,000,000       제 48 기  2016.01.01 ~ 2016.12.31   29,240,672,000,000  25
11  20190401004781      11011      2018  00126380     005930    CFS  연결재무제표  ...    제 49 기  2017.01.01 ~ 2017.12.31   56,195,967,000,000       제 48 기  2016.01.01 ~ 2016.12.31   30,713,652,000,000  27
12  20190401004781      11011      2018  00126380     005930    CFS  연결재무제표  ...    제 49 기  2017.01.01 ~ 2017.12.31   42,186,747,000,000       제 48 기  2016.01.01 ~ 2016.12.31   22,726,092,000,000  29

[4 rows x 20 columns]

result_01 과 result_02의 데이터는 모두 같다. == 연산자를 사용하여 각 필드 데이터를 비교할 수 있다.

print(result_01 == result_02)

OUTPUT

    rcept_no  reprt_code  bsns_year  corp_code  stock_code  fs_div  fs_nm  sj_div  ...  thstrm_amount  frmtrm_nm  frmtrm_dt  frmtrm_amount  bfefrmtrm_nm  bfefrmtrm_dt  bfefrmtrm_amount   ord       
9       True        True       True       True        True    True   True    True  ...           True       True       True           True          True          True              True  True       
10      True        True       True       True        True    True   True    True  ...           True       True       True           True          True          True              True  True       
11      True        True       True       True        True    True   True    True  ...           True       True       True           True          True          True              True  True       
12      True        True       True       True        True    True   True    True  ...           True       True       True           True          True          True              True  True       

[4 rows x 20 columns]

 

주요컬럼 추출

    # 계정과목명, 당기금액, 전기금액 추출
    _extract_cols = ['account_nm', 'thstrm_amount', 'frmtrm_amount']
    extracted_df = result_01.loc[:, _extract_cols]
    account_nm        thstrm_amount        frmtrm_amount
9          매출액  243,771,415,000,000  239,575,376,000,000
10        영업이익   58,886,669,000,000   53,645,038,000,000
11  법인세차감전 순이익   61,159,958,000,000   56,195,967,000,000
12       당기순이익   44,344,857,000,000   42,186,747,000,000

보고자 했던 데이터가 깔끔하게 추출되었다! 이제 이것으로 무엇을 하고 싶은가? 전기 대비 당기 금액 증감율을 보고 싶지 않은가? 

위에 info() 함수를 통해 확인했듯이 모든 컬럼은 object 타입이다. 이를 변환하기 위해서 astype 함수를 사용한다. 인자로써 변환할 형을 지정하게 되는데 이때 int가 아닌 numpy 모듈에서 제공하는 int64를 사용한다. 이유는 변환하는 대상의 값이 int 허용범위를 넘기 때문이다. 

    # numpy 모듈 상단 추가
    import numpy as np
    
    ... 중략 ... 코드 마지막 이어서.
    
    # comma(,) 제거 후 np.int64 파싱
    extracted_df.loc[:, ['thstrm_amount', 'frmtrm_amount']] = extracted_df[['thstrm_amount', 'frmtrm_amount']].apply(lambda x: x.str.replace(',', '').astype(np.int64))

    extracted_df.loc[:,'ratio'] = extracted_df.thstrm_amount / extracted_df.frmtrm_amount * 100

    extracted_df.loc[:,'gap'] = extracted_df.thstrm_amount - extracted_df.frmtrm_amount

    print(extracted_df)

apply 함수를 사용하여 각 Series에 comma(,) 를 제거하고 np.int64 타입으로 변환한다. 

    account_nm    thstrm_amount    frmtrm_amount    ratio            gap
9          매출액  243771415000000  239575376000000  101.751  4196039000000
10        영업이익   58886669000000   53645038000000  109.771  5241631000000
11  법인세차감전 순이익   61159958000000   56195967000000  108.833  4963991000000
12       당기순이익   44344857000000   42186747000000  105.116  2158110000000

 

반응형