23년 8월의 공부 - log 기록, 코드 병목 확인, 지정된 파일 경로에 text 생성
포스트
취소

23년 8월의 공부 - log 기록, 코드 병목 확인, 지정된 파일 경로에 text 생성

2023년 8월 동안 검색하고 공부한 것들을 정리한 내용입니다.

logging

내장 모듈 logging을 이용해 log를 기록할 수 있습니다. python에서는 DEBUG, INFO, WARNING, ERROR, CRITICAL - 기본 5개의 로깅 수준이 있습니다. DEBUG가 가장 낮은 심각도(severity)를 나타내며 CRITICAL이 가장 높은 위험도 수준을 의미합니다. 기본적으로 WARNING 이상의 로그 내용이 기록되게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import logging

logging.basicConfig(
    filename=HOME_PATH + "log/example.log",
    format='%(asctime)s %(levelname)s:%(message)s',
    level=logging.WARNING,
    datefmt='%Y-%m-%d %H:%M:%S',
    # filemode="w",
)

logging.info('Info')
logging.warning('Warning')
logging.error('Error')
logging.critical('Critical')

## example.log
# 2023-08-27 21:02:54 WARNING:Warning
# 2023-08-27 21:02:54 ERROR:Error
# 2023-08-27 21:02:54 CRITICAL:Critical

위 예제에서는 log level을 WARNING으로 설정했기 때문에 INFO 레벨의 log는 기록되지 않았습니다. 또한, filemode="w"로 옵션을 설정한다면 작업을 실행할 때마다 log 파일을 overwrite 합니다.

위의 예시는 log 작성의 간단한 예시이며, 이외에 상황에 맞게 Handler, Formatter를 적용해 사용할 수 있습니다.

code 병목 확인하기 w/ line_profiler

line_profiler는 코드 별 실행 시간을 확인하는 툴 중 하나입니다. py 파일이나 ipynb 파일 모두에서 사용 가능하며, 이름에서 알 수 있듯이 코드의 각 line 별로 수행되는 시간을 제공해주는 역할을 합니다.

py 파일

실행 시간을 확인하려는 코드를 함수로 정의하고 이를 실행하는 py 파일을 생성합니다. 해당 함수가 정의되는 부분에서 @profile 데코레이터를 선언합니다. 2개 이상의 함수를 정의해 사용하는 경우에는 각 함수 앞에 데코레이터를 선언해줍니다.

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
# main.py
import numpy as np
import pandas as pd
import string
import random


@profile
def create_rand_df(n):
    rint = [random.randint(0, 100) for i in np.arange(n)]
    rtext1 = [
        "".join(random.choices(string.ascii_lowercase + string.digits, k=10))
        for i in np.arange(n)
    ]
    df = pd.DataFrame(
        np.stack([np.array(rint), np.array(rtext1)], axis=1),
        columns=["val_int", "val_str"],
    )

    df["val_int"] = df["val_int"].astype("int")
    return df


if __name__ == "__main__":
    create_rand_df(100000)

터미널에서 kernprof -lv {py 파일명}를 실행합니다. 아래 결과를 보면 시간 단위는 $10^{-6}$(=1e-06)초이며, ‘rtext1’을 정의하는 부분이 전체 실행 시간의 약 55%를 차지하며 시간이 오래 걸림을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
> kernprof -lv main.py 
Wrote profile results to main.py.lprof
Timer unit: 1e-06 s

Total time: 0.357017 s
File: main.py
Function: create_rand_df at line 7

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     7                                           @profile
     8                                           def create_rand_df(n):
     9         1     129024.0 129024.0     36.1      rint = [random.randint(0, 100) for i in np.arange(n)]
    10         2     195786.0  97893.0     54.8      rtext1 = [
    11                                                   "".join(random.choices(string.ascii_lowercase + string.digits, k=10))
    12         1         65.0     65.0      0.0          for i in np.arange(n)
    13                                               ]
    14         2       9279.0   4639.5      2.6      df = pd.DataFrame(
    15         1      19084.0  19084.0      5.3          np.stack([np.array(rint), np.array(rtext1)], axis=1),
    16         1          0.0      0.0      0.0          columns=["val_int", "val_str"],
    17                                               )
    18                                           
    19         1       3779.0   3779.0      1.1      df["val_int"] = df["val_int"].astype("int")
    20         1          0.0      0.0      0.0      return df

ipynb 파일

실행 시간을 확인하려는 코드를 함수로 정의합니다.

1
2
3
4
5
6
7
def create_rand_df(n):
  rint = [random.randint(0,100) for i in np.arange(n)]
  rtext1 = [''.join(random.choices(string.ascii_lowercase + string.digits, k=10)) for i in np.arange(n)]
  df = pd.DataFrame(np.stack([np.array(rint), np.array(rtext1)], axis=1), columns=['val_int', 'val_str'])

  df['val_int'] = df['val_int'].astype('int')
  return df

liner_profiler를 import하는 magic command를 실행합니다.

1
%load_ext line_profiler

%lprun -f {정의한 함수명} {실행하려는 함수}를 실행합니다.

1
%lprun -f create_rand_df create_rand_df(100000)

이러한 툴에 대해서 더 알고 싶으시면 cProfile도 있으니 참고해보셔도 좋습니다.

pathlib.Path.write_text

지정된 파일 경로에 텍스트를 작성하는 method 입니다. 파일 경로가 존재하지 않는 경우에는 새롭게 파일을 생성해 텍스트를 작성하며, 파일 경로가 존재하는 경우에는 overwrite하게 됩니다.

1
2
3
4
5
6
from pathlib import Path

file_path = Path("example.txt")
file_path.write_text("Hello World!")
file_path.read_text()
# Hello World!
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.