Pandas dataframe 데이터 처리 방법의 속도 비교

데이터 수정은 주로 첨삭 개차를 위주로 하는데 여기서 몇 가지 작법이 데이터 처리 시간에 있어서의 커다란 차이를 비교한다.
데이터 양은 대략 500만 줄 수준의 데이터로 파일 크기는 100M이다.

1.iloc


iloc는 속도가 매우 느린 작법이다.여기에서 우리는 모든 csv 파일의 줄마다 순환을 다시 iloc로 처리합니다. 예시 코드는 다음과 같습니다.

for index in range(len(df)):
   df.iloc['attr'][index] = xxx
이런 방법으로 500만 줄의 데이터를 처리하는 데는 대략 5시간이 걸리는데 정말 느리다.

2.at


at는 iloc에 비해 성능이 크게 향상되었고 for 순환 처리이기도 하다. 예시 코드는 다음과 같다.

for i in range(len(df)):
  if df.at[i,'attr'] > 0:
    sum_positive += df.at[i,'attr']
  else:
    sum_negetive += df.at[i,'sttr']
내 프로그램에서at와iloc는 통용될 수 있다.at를 사용하면 프로그램의 속도가 대폭 향상될 것이다. 대략 10분 정도이지만 아직 부족하다.

3.apply(lambda x:...)


apply는 for 순환 속도가 너무 느려서 순환적으로 프로그램을 최적화하고 싶다고 말하고 싶습니다.그리고 인터넷에서 어떤 사람들은 apply가 속도를 대폭 높일 수 있다고 말했지만 테스트를 통해 내 프로그램에서 apply와 for를 사용하는 것은 차이가 많지 않고 성능도 보통이라는 것을 발견했다.

4. 시리즈로 직접 처리


이것이야말로 for 순환을 진정으로 최적화하는 방법이다. 위at의 프로그램을 예로 들면 다음과 같이 고칠 수 있다.

sum_positive += df['attr'][df.attr > 0].sum()
sum_negative += df['attr'][df.attr < 0].sum()
프로그램을 모두 시리즈 처리로 바꾸니 훨씬 빨라졌다. 마지막 500만 줄의 데이터는 대략 37초가 걸려서 거의 기대에 부합된다.
여기에 데이터 프레임 속성 선별, 즉 위의 df에 관한 두 마디를 제시합니다.attr > 0 섹션입니다.우선pandas라는 속성 선별은 정말 강력하고 편리합니다.
그 다음은 우리 속성을 선별할 때 속성을 수정하지 말고 뒤에 있는 숫자를 수정하는 것이다. 예를 들어 우리는 이렇게 쓰지 않는다.
float (df.attr) > 0 대신 다음과 같이 쓰십시오.
df.attr>str(0), df 때문에.attr는 속성으로서 함부로 움직일 수 없습니다.
추가: pandas에서 DataFrame 단일 데이터 추출 효율 및 수정 효율

목표


판다스를 사용하여 금융 데이터와 모델링을 처리하는 데 시간 순서에 따라 DataFrame 데이터에 따라 구체적인 위치의 데이터 판단을 읽거나 수정해야 한다.경험상 이런 조작은 직접 2차원 목록이나 np에 대한 것보다 낫다.array 형식의 데이터가 느린 이유는 index와columns 차원의 검색(두 사전이 연속적인 그룹이 아니라 매번 위치를 찾을 때마다 시간이 소요됨)과 DataFrame에서 데이터의 메모리 레이아웃에 있기 때문에 나중에 깊이 연구할 기회가 있습니다.
여기서 수치 실험을 해서 몇 가지 방법의 효율을 비교한다.

데이터 생성


선생은 2차원 그룹의 랜덤 수를 DataFrame 데이터로 삼아 일반성을 잃지 않고 열 이름, 줄 이름을 표시 순서의 문자열로 설정합니다.

import numpy as np
import pandas as pd

from copy import deepcopy
from time import time

np.random.seed(20000)
I = 900
df = pd.DataFrame(np.random.standard_normal((I, I)),
   columns=['c'+str(_) for _ in range(I)],
         index=['i'+str(_) for _ in range(I)])
그리고 한정된 범위 내에서 랜덤으로 수치 위치를 생성하여 비교하기 편리하도록 랜덤 좌표와 문자열 이름을 대응한다

columns_num = np.floor(np.random.uniform(0, 1, I) * I).astype(int)
index_num = np.floor(np.random.uniform(0, 1, I) * I).astype(int)

columns_str = ['c'+str(_) for _ in columns_num]
index_str = ['i'+str(_) for _ in index_num]

읽기 테스트


우선 전통적인 방법은columns 및 index의 명칭을 직접 취하여 위치를 정한다

t0 = time()
for m in columns_str:
  for n in index_str:
    c = df[m][n]
print(time()-t0)

6.789840459823608
먼저columns 열 이름 후values에서 행 좌표를 취하면 속도가 좀 빨라진다

t0 = time()
for m in columns_str:
  for n in index_num:
    c = df[m].values[n]
print(time()-t0)

1.9697318077087402
loc 방법, 속도와 직접columns 및 index의 명칭 포지셔닝 차이가 많지 않음

t0 = time()
for m in columns_str:
  for n in index_str:
    c = df.loc[n, m]
print(time()-t0)

5.661889314651489
at 방법, loc보다 좀 빨라요.

t0 = time()
for m in columns_str:
  for n in index_str:
    c = df.at[m, n]
print(time()-t0)

3.3770089149475098
가령 구체적인 가로 좌표를 알고 나면 우리는 다시 비교한다.
아니면 values를 가져오는 것부터 시작해도 느린 것 같습니다. df에서 values를 가져오는 데 시간이 많이 걸리는 것 같습니다.

t0 = time()
for m in columns_num:
  for n in index_num:
    c = df.values[n][m]
print(time()-t0)

6.041872024536133
iloc 해봐, 다를 게 없어.

t0 = time()
for m in columns_num:
  for n in index_num:
    c = df.iloc[n, m]
print(time()-t0)

6.103677034378052
iat 대비

t0 = time()
for m in columns_num:
  for n in index_num:
    c = df.iat[n, m]
print(time()-t0)

4.375299692153931
마지막으로 가장 효율적인 방법은 우선 2차원 그룹을 추출하여 위치를 정하는 것이다

t0 = time()
b = df.values
for m in columns_num:
  for n in index_num:
    c = b[n][m]
print(time()-t0)

0.6402544975280762

테스트 수정


방금 전의 과정을 반복하여 대응값을 0으로 간단한 테스트 방식으로 바꾸고, 원시 데이터는 백업해야 한다는 것을 잊지 마라
columns 및 index에서 이름 찾기

df_backup = deepcopy(df)
t0 = time()
for m in columns_str:
  for n in index_str:
    df_backup[m][n] = 0.0
print(time()-t0)

41.99269938468933
선columns 열 이름 후values에서 행 좌표 찾기

df_backup = deepcopy(df)
t0 = time()
for m in columns_str:
  for n in index_num:
    df_backup[m].values[n] = 0.0
print(time()-t0)

2.215076208114624
loc 방법

df_backup = deepcopy(df)
t0 = time()
for m in columns_str:
  for n in index_str:
    df_backup.loc[n, m] = 0.0
print(time()-t0)

134.39290761947632
at 방법, 수정 수치가 loc보다 이렇게 빠르다니

df_backup = deepcopy(df)
t0 = time()
for m in columns_str:
  for n in index_str:
    df_backup.at[n, m] = 0.0
print(time()-t0)

4.7453413009643555
values에서 바꾸는 것도 괜찮습니다. 읽기와 비슷합니다. 매번 values를 추출하는 데 시간이 걸리는 것 같습니다.

df_backup = deepcopy(df)
t0 = time()
for m in columns_num:
  for n in index_num:
    df.values[n][m] = 0.0
print(time()-t0)

6.346027612686157
iloc 방법

df_backup = deepcopy(df)
t0 = time()
for m in columns_num:
  for n in index_num:
    df.iloc[n, m] = 0.0
print(time()-t0)

122.33384037017822
iat 방법

df_backup = deepcopy(df)
t0 = time()
for m in columns_num:
  for n in index_num:
    df.iat[n, m] = 0.0
print(time()-t0)

5.381632328033447
2차원 그룹을 찾아서 위치를 정하다

df_backup = deepcopy(df)
t0 = time()
b = df.values
for m in columns_num:
  for n in index_num:
    c = b[n][m]
print(time()-t0)

0.4298992156982422

총결산


효율상 직접 수치가 가장 좋은 것이 틀림없다. 이번에는 체계적인 비교를 통해 기록한다.코드를 좀 지루하게 썼지만, 복제 실험이 편리하다.모델링 단계의 코드에 있어서 나는 두 번째 방법을 사용하는 데 익숙하다. 주로 코드의 가독성, 유지보수와 수정을 감안한다.코드는 키에서 나에게 여기가 무엇인지 알려주고 직관적이고 읽기 쉽다.
이전에도 코드 운행 효율을 높이기 위해 2차원 그룹을 추출하는 것을 쓴 적이 있지만,columns가 많으면 매우 힘들기 때문에 다시 한 번 읽어야 한다.물론 데이터를 클래스로 쓸 수도 있지만 판다스와 잘 융합되지 않아 모델링과 연구 효율에 있어 좋은 해결 방안이 없다.나중에 시간을 내서 DataFrame 내부 메커니즘을 연구하겠습니다.
이상의 개인적인 경험으로 여러분께 참고가 되었으면 좋겠습니다. 또한 많은 응원 부탁드립니다.만약 잘못이 있거나 완전한 부분을 고려하지 않으신다면 아낌없이 가르침을 주시기 바랍니다.

좋은 웹페이지 즐겨찾기