TIL 156일차: Streamlit

2024. 6. 6. 19:35회고/TIL(매일)

 

✏️도전한 점


1. Streamlit 코드 작성

2. 에러코드 0을 해결하고자 노력함.

 

01 Streamlit 에러메세지 해결


DuplicateWidgetID: There are multiple identical st.button widgets with the same generated key. When a widget is created, it's assigned an internal key based on its structure. Multiple widgets with an identical structure will result in the same internal key, which causes this error. To fix this error, please pass a unique key argument to st.button.

 

코드 작성 중 위와 같은 에러 메세지가 떴다. 원인은 Streamlit 애플리케이션에서 st.button 위젯이 동일한 구조로 여러 번 생성되어 같은 내부 키가 할당되었기 때문에 발생한다.

 

💡해결 방법은 각 버튼에 고유한 키를 지정하면 된다.

 

즉, 버튼을 구현할 때 각각을 구분해주지 않으면 경우에 따라 문제가 발생한다. (없어도 잘 될 수 있음.) 그동안 key값이 어떻게 쓰이나 했더니 지금에서야 쓰인다. st.button이 있는 코드 마지막에 하이퍼 파라미터를 추가하면 되는 간단한 방법이었다.

 

 

02 중복 가게명 문제 해결


수집한 데이터를 최대한 활용하고자 가게 리뷰 별 군집을 부여하였기 때문에 (최빈값 사용X) 버튼을 눌렀을 때 중복으로 나타나는 가게명이 존재했다. ex)태극당 처럼 리뷰가 많을수록 자주 보였다. 이러한 문제를 해결하고자 한다.

 

1) 가게를 랜덤하게 선출하기 위하여 sample() 메소드를 사용했다. sample()은 중복 없이 행을 뽑아내는 기능을 한다. 즉, 한 번 뽑힌 행은 다시 뽑힐 수 없는 것이다. 같은 가게명이라도 다른 리뷰니까 다른 객체라고 인식했다. 이부분에서는 sample()을 사용할 수 없었다.

 

2) values를 counter하는 방법을 사용하고자 했다. 그렇다면 총 두 가지 방법이 존재한다.

 

첫째, 시리즈기 때문에 value_counts() 메소드 사용이 가능하다.
둘째, collection의 Counter() 클래스를 사용하는 것이다. 클래스 객체.values()를 하면 된다.

 

이 둘을 이용하여 뽑아낸 가게명 리스트 counter max값이 1이면 OK 그만 선출해라고 명령하면 될 것이다. 코드는 다음과 같다. 중복인 가게명이 존재하는 예시를 건내주니 counter값 중에 2가 있다.

 

 

*참고: Counter을 사용하면 자료형이 counter 변수 단계에서 딕셔너리 > values()메소드를 사용하면 딕셔너리 객체(리스트형) > list를 통해 리스트화 순서로 총 3단계 변환 과정을 거친다.

 

 

그럼 둘 중 무엇을 사용해면 좋을까? import time의 time.time()을 사용하면 아주 미세한 차이를 잡아내기 어렵다.

 

💡방법은 time.pref_counter()를 사용하는 것이다. 종료시각에서 시작시각을 빼면 두 코드의 차이를 알려주는데 역시 클래스를 사용한 코드가 약 4배정도 빨랐다. (내가 대단한 양의 자료를 다루는 건 아니지만 큰 차이를 발견했다.)

 

결과적으로 아래의 세 가지 코드를 사용하여 중복가게 출력 문제를 해결했다.

1. Counter() 클래스

2. while 반복문

3. 종료를 위한 flag변수

 

03 클러스터별 네이밍 태그 작업 (피쳐 엔지니어링)


💡중요💡딕셔너리+map()함수로 클러스터별 네이밍 태그 파생변수 제작했다. 물론 apply도 가능하다. map을 사용한 이유는 훨씬 간단하기 때문이다. map은 백준 문제를 풀 때 변수A, 변수B = map(int, input().split()) 코드와 같이 각각의 변수에 값을 받을 때 자주 사용해서 익혀보려고 했는데 이렇게 전처리에 사용하게 되니 정말 유용한 함수였다.

 

df['cluster_labeling'] = df['cluster'].map(labeling)

 

여기에서 labeling은 군집별 네이밍 태그를 붙인 딕셔너리 자료형이다. 새로운 컬럼이름.map(딕셔너리)만으로도 사용자 정의 함수 없이 간단하게 제작할 수 있어서 좋았다. apply사용 코드도 써봤다. 방법은 아래와 같다.

 

1. 클러스터별 네이밍을 위해 사용자 정의 함수를 제작한다.
def cluster_naming(cluster):
    if cluster == 0:
        return '아주 좋음'
    elif cluster == 1:
        return '좋음'
    elif cluster == 2:
        return '보통'
    else:
        return '나쁨'

2. apply로 셀마다 함수를 적용시키고 파생 컬럼을 생성한다.
df['cluster_name'] = df['cluster'].apply(cluster_naming)

 

04 조건부 값=0이라 출력을 못하겠다는 문제


정보값을 출력할 때, 사용자가 선택한 군집 번호를 그대로 가져가서 출력에 적용하고 싶었다. 하지만 분명 맞는 코드인데 값이 없으니 출력을 못하겠다는 메세지가 떴다. 이번 문제는 아예 그전 단계로 건너뛰고 information 변수 생성 단계에서 cond를 통해 조건을 추가해서 조정했더니 해결됐다.