namedtuple 사용기 & globals 탈출기

DanA·2022년 9월 3일
2

python

목록 보기
2/4

잘못된 코드 : globals()로 변수 저장

원래 globals()는 디버깅용으로만 사용해야하는데, 그동안 변수를 만드는 데 globals()를 사용해왔다.
예를들면 다음과 같이...

import itertools
id_list = 'HLA-A HLA-B HLA-C'.split()
number_list = '9 10'.split()

for id, number in itertools.product(*[id_list, number_list]):
    Dataframe = df_[df_['allele'].str.contains(id)]
    Dataframe = df_[df_['length']==int(number)]
    globals()[f'df_{id}_{number}'] = Dataframe

이러면 globals()[f'df_HLA-A-9']부터 globals()[f'df_HLA-C-10']까지 위 코드의 로직대로 Dataframe type의 변수가 생성된다.
얼핏보면 변수이름을 일일이 지정해주지 않아도 되고 굉장히 간편해보인다.
하지만 여기엔 많은 문제점이 있음! 예를들면 그 코드 자체를 돌릴 땐 문제가 되지 않는데, 저장해두고선 모듈로 불러올 때도 문제가 되고.. 등등 아무튼 여러가지 문제가 있다.
그래서 좋은 코드에선 globals()는 디버깅용으로만 사용하고, 이런 목적(dataframe 저장)으로 사용하고자 할 땐 리스트, 튜플, 딕셔너리 등의 자료구조에 저장을 해서 불러오는 식으로 사용을 한다고 한다.

수정한 코드 : namedtuple 사용기

따라서 본인은 이번에 namedtuple을 사용하여 구현해보았다.

import itertools

Test = namedtuple('MakeNamedtuple', ['id', 'number', 'Dataframe'])

test_list = []
for id, number in itertools.product(*[id_list, number_list]):
    Dataframe = df_[df_['allele'].str.contains(id)]
    Dataframe = df_[df_['length']==int(number)]
    test_list.append(Test(id, number, Dataframe))

이렇게 구현을 했다. 이제 몇 가지 확인을 통해 test_list를 살펴보자.

print(len(test_list))
print(test_list[0].id)
# 결과
6
HLA-A-9
df_1 = test_list[0].Dataframe
df_2 = globals()[f'df_HLA-A_9']
print(df_1[(df_1 == df_2).all(axis=1) == False]) # 두 데이터프레임에서 서로 다른 요소가 있는지 확인하는 방법
# 결과
Empty DataFrame

전역변수를 사용하여 dataframe을 변수에 할당한 것과 namedtuple을 사용한 것이 일치하게 나왔다. 데이터를 관리하기도 훨씬 편하다.

결론 : namedtuple씁시다.

p.s. 참고로 namedtuple로 된 걸 그냥 저장하려하면 에러뜸.
cloudpickle이나 dill을 사용하거나..
전역변수 지역변수 문제로 인해 에러가 뜨므로 저장을 하려면 namedtuple class 자체를 함수 내에서 구현하고 저장해야됨.
=> 사실 뭔 말인지 아직 잘 모르겠음. 일단 돌아가니까 그냥 쓰고 시간남을 때 다시 공부 링크

분석에 사용한 최종 코드

from collections import namedtuple
import dill
import itertools
import pandas as pd

def SavePredictionList(df):
    
    allele_list = 'HLA-A,HLA-B,HLA-C'.split(',')
    length_list = '9,10'.split(',')

    SavePred = namedtuple('Prediction', ['id', 'pred'])
    prediction_list = []
    
    for allele, length in itertools.product(*[allele_list, length_list]):
        id = allele + '_' + length
        total = df[df['allele'].str.contains(allele)]
        total = df[df['length']==int(length)]
        abe = SavePred(id, total)
        prediction_list.append(abe)
            
        with open(f'/home/hb/python/DeepNeo/data/result/1st_pred/{allele}_{length}.dill', 'wb') as f:
            dill.dump(abe, f)
            
        return prediction_list
        
prediction_list = SavePredictionList(df=merged)
profile
단아와라라

0개의 댓글