본문 바로가기
🐍Python

[20210804] Pandas 라이브러리 - DataFrame(+ 시계열 데이터 다루기)

by 캔 2021. 8. 4.

DataFrame

지난 글에 이어서 Pandas 라이브러리의 DataFrame 모듈에 대해 살펴보려고 한다.

# Pandas.DataFrame - 2
# 특정 값 가져오기
from pandas import DataFrame

data = [
    ["0010", "Hong", 250, 5.5],
    ["0020", "Lee", 300, 6.5],
    ["0030", "Kang", 280, 6.0],
]
columns = ['emno', 'ename', 'sal', 'comm']
df = DataFrame(data=data, columns=columns)
df = df.set_index('emno')
print(df.iloc[0].iloc[1])
print(df.iloc[0][1])
print(df.iloc[0].loc['sal'])
print(df.iloc[0]['sal'])

# loc 속성에 로우 인덱스와 칼럼의 레이블을 순서대로 입력하면 특정 로우와 칼럼에 값을 접근할 수 있다.
print(df.loc["0010", "sal"])
print(df.iloc[0, 1])

# 데이터 프레임의 칼럼에 우선 접근하고 로우를 선택
print(df["sal"].loc["0010"])
print(df["sal"]["0010"])
print(df["sal"].iloc[0])
print(df["sal"][0])

# 특정 범위 가져오기
from pandas import DataFrame

data = [
    ["0010", "Hong", 250, 5.5],
    ["0020", "Lee", 300, 6.5],
    ["0030", "Kang", 280, 6.0],
]
columns = ['emno', 'ename', 'sal', 'comm']
df = DataFrame(data=data, columns=columns)
df = df.set_index('emno')
print(df.loc[["0010", "0020"]])
print(df.iloc[[0, 1]])

# 칼럼 인덱싱으로 해당하는 칼럼 데이터만 선택해서 출력
df = df.loc[["0010", "0020"]]
print(df[["sal", "comm"]])

# 로우 인덱스와 칼럼 인덱스를 지정하면 한 번에 특정 범위의 데이터만 선택 가능. 단, 로우 인덱스 먼저 지정
print(df.loc[["0010", "0020"], ["ename", "sal"]])
print(df.iloc[[0, 1], [0, 1]])

# 불리언 인덱싱 응용해서 특정 조건을 만족하는 값들을 선택할 수 있다. 로우 인덱스에 조건 비교를 적용
from pandas import DataFrame

data = [
    ["0010", "Hong", 250, 5.5],
    ["0020", "Lee", 300, 6.5],
    ["0030", "Kang", 280, 6.0],
]
columns = ['emno', 'ename', 'sal', 'comm']
df = DataFrame(data=data, columns=columns)
df = df.set_index('emno')
cond = df["sal"] > 250
print(df.loc[cond, "sal"])

# 칼럼 추가하기
from pandas import DataFrame

data = [
    ["0010", "Hong", 250, 5.5],
    ["0020", "Lee", 300, 6.5],
    ["0030", "Kang", 280, 6.0],
    ["0040", "Yang", 290, 6.2],
    ["0050", "Kim", 320, 6.7],
]
columns = ['emno', 'ename', 'sal', 'comm']
index = ["01", "02", "03", "04", "05"]
df = DataFrame(data=data, columns=columns, index=index)
df["ansal"] = df["sal"] * 12 + (df["sal"] * df["comm"] * 0.01)
print(df)

# 로우 추가하기
from pandas import DataFrame, Series

data = [
    ["0010", "Hong", 250, 5.5],
    ["0020", "Lee", 300, 6.5],
    ["0030", "Kang", 280, 6.0],
    ["0040", "Yang", 290, 6.2],
    ["0050", "Kim", 320, 6.7],
]
columns = ['emno', 'ename', 'sal', 'comm']
index = ["01", "02", "03", "04", "05"]
df = DataFrame(data=data, columns=columns, index=index)
df.loc["06"] = Series(["0060", "Jang", 270, 5.9], index=columns)
print(df)

# 데이터프레임에 로우 추가: append()
choi = ["0070", "Choi", 280, 6.0]
s = Series(data=choi, index=columns, name="07")
new_df = df.append(s)
print(new_df)

# 데이터프레임 객체를 이용
choi = [["0070", "Choi", 280, 6.0]]
row_df = DataFrame(data=choi, index=["07"], columns=columns)
new_df = df.append(row_df)
print(new_df)

# 칼럼 삭제하기
from pandas import DataFrame

data = [
    ["0010", "Hong", 250, 5.5],
    ["0020", "Lee", 300, 6.5],
    ["0030", "Kang", 280, 6.0],
    ["0040", "Yang", 290, 6.2],
    ["0050", "Kim", 320, 6.7],
]
columns = ['emno', 'ename', 'sal', 'comm']
index = ["01", "02", "03", "04", "05"]
df = DataFrame(data=data, columns=columns, index=index)

df2 = df.drop("emno", axis=1)  # axis=1 칼럼의 레이블을 의미, 칼럼 emno를 삭제
print(df)
print(df2)

# 로우 삭제하기 axis=0는 로우 값을 삭제할 때 쓴다.
df3 = df.drop("05", axis=0)
print(df)
print(df3)

# 원본 데이터프레임에서 로우나 칼럼을 바로 삭제하려면 inplace 항목에 True 값을 넘겨주면 된다.
df.drop("05", axis=0)  # 삭제 안 됨
print(df)
df.drop("05", axis=0, inplace=True)
print(df)

# 여러 로우를 삭제
df.drop(["04", "03"], axis=0, inplace=True)
print(df)

# 칼럼 삭제
df.drop("emno", axis=1, inplace=True)
print(df)

# 여러 칼럼 삭제
df.drop(["sal", "comm"], axis=1, inplace=True)
print(df)

# 칼럼 레이블 변경
from pandas import DataFrame

data = [
    ["0010", "Hong", 250],
    ["0020", "Lee", 300],
    ["0030", "Kang", 280],
    ["0040", "Yang", 290],
    ["0050", "Kim", 320],
]
columns = ['emno', 'ename', 'sal']
df = DataFrame(data=data, columns=columns)
df = df.set_index('emno')

print(df.columns)  # columns 속성에 바인딩(대입)된 값을 출력
print(df.index)  # index 속성에 바인딩(대입)된 값을 출력

df.columns = ['name', 'pay']  # ename -> name, sal -> pay 변경
df.index.name = 'code'  # emno -> code 변경됨.
print(df)

# 데이터 타입 변경
from pandas import DataFrame
import numpy as np

data = [
    ["0010", "Hong", '2,500'],
    ["0020", "Lee", '3,000'],
    ["0030", "Kang", '2,800'],
]
columns = ["emno", 'ename', 'sal']
df = DataFrame(data=data, columns=columns)


def remove_comma(x):
    return x.replace(',', '')


df['sal'] = df['sal'].apply(remove_comma)
print(df)
print(df.dtypes)  # object로 리턴 -> 문자열 타입과 같다. '모든 타입이 문자열로 되어 있다'라는 의미
df = df.astype({'sal': np.int64})  # astype() 매개변수 딕셔너리의 key, value로 표현된다.
print(df)  # np.int64: numpy 모듈의 정수 타입
print(df.dtypes)

# 정렬
from pandas import DataFrame

data = [
    ["0010", "Hong", 250],
    ["0020", "Lee", 300],
    ["0030", "Kang", 280],
]
columns = ["emno", 'ename', 'sal']
df = DataFrame(data=data, columns=columns)
df = df.set_index('emno')
df2 = df.sort_values(by='sal')  # 오름차순 정렬
df3 = df.sort_values(by='sal', ascending=False)  # 내림차순 정렬
print(df2)
print(df3)

# 순위
df['rank'] = df['sal'].rank()
print(df)
df4 = df.sort_values(by='rank')
df5 = df.sort_values(by='rank', ascending=False)
print(df4)
print(df5)

# 위/아래 붙이기
# 두 개의 데이터프레임이 칼럼이 같을 때 위/아래로 붙여서 새로운 데이터프레임 생성 가능
from pandas import DataFrame

data = [
    ["0010", "Hong", 250],
    ["0020", "Lee", 300],
]
columns = ['emno', 'ename', 'sal']
df1 = DataFrame(data=data, columns=columns)
df1 = df1.set_index('emno')

data = [
    ["0030", "Kang", 280],
    ["0040", "Kim", 290],
]
columns = ['emno', 'ename', 'sal']
df2 = DataFrame(data=data, columns=columns)
df2 = df2.set_index('emno')

df = df1.append(df2)
print(df)

# 메서드를 이용해서 붙이기: concat()
# 문자열 + 문자열 붙여서 출력하기
import pandas as pd

df = pd.concat([df1, df2])  # df1 데이터프레임과 df2 데이터프레임을 붙여서 새로운 데이터프레임 df를 생성
print(df)

# 옆으로 붙이기: 인덱스가 동일한데 데이터가 칼럼이 분리되어 있을 때 붙여서 하나의 데이터프레임으로 생성
from pandas import DataFrame
import pandas as pd

# 첫 번째 데이터 프레임
data = [
    ["Hong", "0010"],
    ["Lee", "0020"],
]
columns = ["ename", "emno"]
index = ['01', '02']
df1 = DataFrame(data=data, columns=columns, index=index)

# 두 번째 데이터 프레임
data = [
    [6.0, 280],
    [6.5, 300],
]
columns = ["comm", "sal"]
index = ['01', '02']
df2 = DataFrame(data=data, columns=columns, index=index)
df = pd.concat([df1, df2], axis=1)
print(df)

# 좌우로 붙인 데이터프레임에서 칼럼명이 적당하지 않을 때 칼럼명 순서 바꾸기
# 리스트 인덱싱 기호 [] 안에 넣어주면 됨.
order = ['emno', 'ename', 'sal', 'comm']
df = df[order]
print(df)

# 데이터 프레임을 엑셀로 저장: to_excel()
# sheet_name 속성에 엑셀 시트 이름을 지정
from pandas import DataFrame

data = [
    ["0010", "Hong", 250, 5.5],
    ["0020", "Lee", 300, 6.5],
    ["0030", "Kang", 280, 6.0],
    ["0040", "Yang", 290, 6.2],
    ["0050", "Kim", 320, 6.7],
]
columns = ['emno', 'ename', 'sal', 'comm']
index = ["01", "02", "03", "04", "05"]
df = DataFrame(data=data, columns=columns, index=index)
df.to_excel("data.xlsx", sheet_name="employees")
# df.to_excel("data.xlsx", sheet_name="employees", index=False) # 인덱스 값 저장 안 함

# 엑셀 데이터 읽어오기
import pandas as pd

df = pd.read_excel("data.xlsx")
print(df)

 

DataFrame으로 시계열 자료 다루기

Pandas로 시계열 데이터를 다뤄보자. 여기서는 한국거래소의 주가 자료들을 조회, 출력할 수 있는 pykrx 모듈을 이용해서 데이터를 얻고 그 데이터를 Pandas 모듈을 이용해서 가공해 본다.

# Pandas로 시계열 데이터 다루기
# Datetime
from pandas import DataFrame

data = [
    [9450, 9490, 9160, 9220, 145794],
    [9380, 9570, 9330, 9490, 57571],
    [9220, 9400, 9100, 9380, 131427],
    [9200, 9250, 9030, 9230, 66893],
    [9350, 9600, 9070, 9200, 138861]
]
columns = ["시가", "고가", "저가", "종가", "거래량"]
index = ["2019-06-07", "2019-06-05", "2019-06-04", "2019-06-03", "2019-05-31"]
df = DataFrame(data=data, index=index, columns=columns)
print(df)
print(df.index)  # dtype='object' -> str 문자열 타입

# 날짜가 문자열로 출력
import pandas as pd

dates = ["2019-06-07", "2019-06-05", "2019-06-04", "2019-06-03", "2019-05-31"]
index = pd.to_datetime(dates)
print(index)  # datetime64[ns] 날짜형 데이터로 변환되었다.
"""
판다스 자료형         파이썬 자료형         설명
object              str(string)         문자열
int64               int                 정수
float64             float               실수
datetime64          datetime            날짜 및 시간
"""

import pandas as pd

dates = ["19/06/07", "19/06/05", "19/06/04", "19/06/03", "19/05/31"]
index = pd.to_datetime(dates)
print(index)

# DatetimeIndex(['2007-06-19', '2005-06-19', '2004-06-19', '2003-06-19',
# '2031-05-19'], dtype='datetime64[ns]', freq=None)
# to_datetime() 함수의 format 인자로 사용자가 사용한 날짜 포맷으로 지정해주면 된다.
# %Y는 4자리 년도, %y 2자리 년도, %m과 %d는 각각 2자리 월과 일, '%y/%m/%d'로 지정하면 된다.
import pandas as pd

dates = ["19/06/07", "19/06/05", "19/06/04", "19/06/03", "19/05/31"]
index = pd.to_datetime(dates, format="%y/%m/%d")
print(index)

# DatetimeIndex(['2019-06-07', '2019-06-05', '2019-06-04', '2019-06-03',
# '2019-05-31'], dtype='datetime64[ns]', freq=None)

# index를 datetime64로 변경
import pandas as pd
from pandas import DataFrame

data = [
    [9450, 9490, 9160, 9220, 145794],
    [9380, 9570, 9330, 9490, 57571],
    [9220, 9400, 9100, 9380, 131427],
    [9200, 9250, 9030, 9230, 66893],
    [9350, 9600, 9070, 9200, 138861]
]
columns = ["시가", "고가", "저가", "종가", "거래량"]
index = ["2019-06-07", "2019-06-05", "2019-06-04", "2019-06-03", "2019-05-31"]
df = DataFrame(data=data, index=pd.to_datetime(index), columns=columns)
print(df.index)  # pd.to_datetime(index), dtype='datetime64[ns]'

# pykrx 모듈: naver와 한국거래소(http://www.krx.co.kr)에서 유가증권 데이터를 스크래핑 후
# 데이터프레임으로 값을 리턴
# pip install -U pykrx
from pykrx import stock

df = stock.get_market_ohlcv_by_date("20190501", "20190531", "005930")  # 삼성전자
print(df.head())  # head() 처음 5개 로우만 가져옴

df = stock.get_market_ohlcv_by_date("20210701", "20210731", "006800")  # 006800: 미래에셋증권
print(df)

# iloc, loc 속성 값을 사용해서 로우에 접근하고 인덱싱을 사용해보자.
print(df.loc["20210730"]["종가"])
print(df.iloc[4][3])
print(df.loc["20210730", "종가"])
print(df.iloc[4, 3])

# 동일 거래일 동안 시가 대비 종가가 상승한 날을 출력
cond = df["종가"] > df["시가"]
print(df[cond])

# 삼성전자: 2018/05/04에 50:1 액면 분할을 실시 250만 원 5만 원으로 변경되었다.
# 수정 종가 자동 반영, 수정종가 반영하고 싶지 않으면 adjusted = False
df1 = stock.get_market_ohlcv_by_date("20180427", "20180506", "005930")
df2 = stock.get_market_ohlcv_by_date("20180427", "20180506", "005930", adjusted=False)
print(df1)
print(df2)

# 칼럼 시프트: 전일거래량, 칼럼을 추가해서 적용하기
from pykrx import stock

df = stock.get_market_ohlcv_by_date("20210701", "20210731", "005930")
df["전날거래량"] = df["거래량"].shift(1)
cond = df["거래량"] > df["전날거래량"]  # 전날 거래량보다 많은 날 구하기
print(df[cond])
print("상승일:", len(df[cond]))
print("영업일:", len(df))

df1 = stock.get_market_ohlcv_by_date("20210101", "20210731", "005930")
cond = df1["종가"] > df1["시가"].shift(-1)
df1[cond].to_excel("data1.xlsx")
print("상승일:", len(df1[cond]))
print("영업일:", len(df1))

# 이동평균: 이동평균선은 각 거래일을 기준으로 계산한 5일 주가 이동평균을 차례로 연결하여 그래프로 표현
# rolling(window=5) 위에서부터 5개의 데이터에 대해 mean() 메서드를 적용시킴
from pykrx import stock

df = stock.get_market_ohlcv_by_date("20180101", "20180531", "005930")
df['5일이동평균'] = df['종가'].rolling(window=5).mean()
print(df)

# 주어진 기간 동안 시가가 5일 이동평균선을 돌파한 횟수 카운트
from pykrx import stock

df = stock.get_market_ohlcv_by_date("20180101", "20180531", "005930")
df['5일이동평균'] = df['종가'].rolling(window=5).mean()
cond = df['5일이동평균'].shift(1) < df['시가']
print("상승일:", len(df[cond]))
print("영업일:", len(df))