DataFrame의 Loop 방법에 대한 고민
백테스트를 하다 보면 DataFrame자료를 가지고 Loop를 돌려야 할 때가 많아서 어떻게 하면 코드를 더 빠르게 만들 수 있을까 하는 고민을 하게 된다.
"무식하게 Loop를 돌리지 말고 Vectorization을 해!" 라고 반문할 수도 있겠지만 Vectorization이 가능한 형태의 코드는 복잡하고 다양한 조건들을 반영해갈 수 없는 것 같다. 실전에서 사용할 트레이딩 전략들은 보통 여러 가지 진입 및 청산 조건들을 동시에 가지고 가면서 직전 시점에서의 단가를 포함한 포지션 정보를 반영해서 다시 roll-forward 방식으로 계산해나갈 수밖에 없는 구조를 가지고 있어서 쉽지가 않다.
준비된 자료는 238674행과 5개의 열을 가진 분봉데이터로 DataFrame으로 변환되어 있는 상태이다. 이 데이터를 iloc, iat, iterrows, itertuples을 활용한 총 네 가지 방법으로 맨 위 행부터 순차적으로 각각 open, high, low, close, volume에 있는 값들을 불러오는 Loop 코드를 작성하고 소요되는 시간을 측정해서 비교할 생각이다.
iloc / iat / iterrows / itertuples 차이
1. Pandas.DataFrame.iloc
인터넷에 떠도는 많은 예전 코드들을 보면 iloc을 사용하는 경우가 많았다. 왜냐하면 iat라는 함수가 존재하지 않던 시절이 있었기 때문이다. 현재는 인덱싱하고자 하는 것이 범위가 아니라 단일 값이라면 함수 표현에도 차이가 없는 iat를 사용하는 것이 절대적으로 옳다. 왜냐하면 iat가 iloc에 비해서 압도적으로 빠르기 때문이다.
여담이지만 가급적 iloc은 사용하지 않는 것이 성능 향상에 도움이 된다. 이를테면 모든 행의 자료를 iloc으로 읽어와서 다시 업데이트해야하는 상황이라면 차라리 기존 DataFrame을 업데이트하지 않고 dictionary형태로 자료를 저장해놓은 다음 최종적으로 연산이 끝난 후 dictionary를 새로운 DataFrame으로 변환하는 것이 더 빠르다.
2. Pandas.DataFrame.iat
DataFrame에서 단일 위치의 값을 인덱싱하는 목적으로는 가장 편하면서도 빠른 방법 중 하나이다. iloc보다는 몇 배나 빠르지만 마찬가지로 모든 데이터 범위를 인덱싱하는 일이 빈번하다면 Pandas의 특성상 전체 속도는 다소 지연될 수 밖에는 없다.
3. Pandas.DataFrame.iterrows
iterrows함수를 사용해서 데이터를 하나씩 출력해보면 아래와 같은 형태로 자료가 반환된다.
tuple형태의 자료이며 그 안의 첫번째에는 DataFrame의 인덱스 값과 그 위치에 해당하는 칼럼 값들이 저장된 series가 반환된다.
4. Pandas.DataFrame.itertuples
itertuples의 경우에는 다소 생소한 형태로 자료가 출력된다.
iterrows는 index와 다른 값들이 하나의 pandas.core.frame.Pandas로 묶여서 나온다. (이걸 Namedtuple이라고 하던데 자세한 건 구글을 참조) 각각의 값에 인덱싱 하기 위해서는 data.Index, data.open, data.high,... 와 같은 형태로 접근해야 하기 때문에 딕셔너리 등의 구조에 익숙하다면 약간 번거로울 수도 있다. 개인적으로는 속도면에서 약간의 손해를 감수하더라도 Dictionary로 변환해서 사용하는 것을 선호한다. 아래와 같이 코드 한 줄만 추가하면 Dictionary형태로 손쉽게 바꿀 수 있다.
iloc / iat / iterrows / itertuples 속도 차이
위의 네 가지 방법으로 DataFrame의 모든 행의 값들을 호출하는데 소요된 시간은 아래와 같다.
전원이 연결되지 않은 I5-1240P 노트북으로 실행한 결과라서 모든 방법들의 속도가 전반적으로 안 좋게 나왔다.
iloc의 속도가 처참할 것은 예상했지만 iat와 iterrows가 비슷하게 나온 건 좀 의외다. 다만 이후 어떤 데이터 처리를 하느냐에 따라 iterrows가 갖는 이점은 분명 존재한다. 어찌됐든 이 테스트는 압도적인 차이로 itertuples로 1위를 차지했다. iterrows와 itertuples는 겉으로 봐서는 대단한 차이가 있는 것 같지 않은데 내부적으로 처리되는 과정의 길이가 엄청난 차이가 존재하는 듯?
이 테스트에 사용된 전체 코드 구성은 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
import sqlite3
import pandas as pd
import timeit
con = sqlite3.connect('database.db')
table_name = '10100000_5'
df = pd.read_sql("SELECT * FROM '" + table_name + "'", con, index_col='index')
def loop_iloc():
for i in range(len(df)):
for column in df.columns:
value = df[column].iloc[i]
def loop_iat():
for i in range(len(df)):
for column in df.columns:
value = df[column].iat[i]
def loop_iterrows():
for row in df.iterrows():
idx = row[0]
for item in row[1]:
value = item
def loop_itertuples():
for row in df.itertuples():
for item in row:
value = item
t1 = timeit.timeit(stmt="loop_iloc()", number=1, globals=globals())
t2 = timeit.timeit(stmt="loop_iat()", number=1, globals=globals())
t3 = timeit.timeit(stmt="loop_iterrows()", number=1, globals=globals())
t4 = timeit.timeit(stmt="loop_itertuples()", number=1, globals=globals())
print(f'iloc 속도: {t1}')
print(f'iat 속도: {t2}')
print(f'iterrows 속도: {t3}')
print(f'itertuples 속도: {t4}')
|
cs |
'Python, API' 카테고리의 다른 글
백테스트의 속도개선(2) (0) | 2022.11.27 |
---|---|
백테스트의 속도개선 (0) | 2022.11.16 |
[XINGAPI] 주식차트조회(일주월) - t8413 (0) | 2022.07.07 |
[XINGAPI] 이베스트투자증권의 분봉 표시 (1) | 2022.07.07 |
[XINGAPI] 해외선물 실시간 주문접수/응답/체결 정보 가져오기 - TC1/TC2/TC3 (0) | 2022.06.30 |