├── .gitignore ├── 00-text.py ├── 01-data.py ├── 02-basic-ui.py ├── 03-lotto.py ├── 04-chart.py ├── 05-file.py ├── 06-stock-chart.py ├── 07-stock-chart-2.py ├── 08-mbti.py ├── 09-clova.py ├── 10-bitly-shorten.py ├── 11-national-pension.ipynb ├── 11-national-pension.py ├── README.md ├── requirements.txt └── your_apikey.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # Data Files 2 | *.csv 3 | *.xls 4 | *.xlsx 5 | *.txt 6 | *.ini 7 | 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | 13 | # C extensions 14 | *.so 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | pip-wheel-metadata/ 31 | share/python-wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | MANIFEST 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .nox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | *.py,cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | 135 | # Pyre type checker 136 | .pyre/ 137 | -------------------------------------------------------------------------------- /00-text.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | # 타이틀 적용 예시 4 | st.title('이것은 타이틀 입니다') 5 | 6 | # 특수 이모티콘 삽입 예시 7 | # emoji: https://streamlit-emoji-shortcodes-streamlit-app-gwckff.streamlit.app/ 8 | st.title('스마일 :sunglasses:') 9 | 10 | # Header 적용 11 | st.header('헤더를 입력할 수 있어요! :sparkles:') 12 | 13 | # Subheader 적용 14 | st.subheader('이것은 subheader 입니다') 15 | 16 | # 캡션 적용 17 | st.caption('캡션을 한 번 넣어 봤습니다') 18 | 19 | # 코드 표시 20 | sample_code = ''' 21 | def function(): 22 | print('hello, world') 23 | ''' 24 | st.code(sample_code, language="python") 25 | 26 | # 일반 텍스트 27 | st.text('일반적인 텍스트를 입력해 보았습니다.') 28 | 29 | # 마크다운 문법 지원 30 | st.markdown('streamlit은 **마크다운 문법을 지원**합니다.') 31 | # 컬러코드: blue, green, orange, red, violet 32 | st.markdown("텍스트의 색상을 :green[초록색]으로, 그리고 **:blue[파란색]** 볼트체로 설정할 수 있습니다.") 33 | st.markdown(":green[$\sqrt{x^2+y^2}=1$] 와 같이 latex 문법의 수식 표현도 가능합니다 :pencil:") 34 | 35 | # LaTex 수식 지원 36 | st.latex(r'\sqrt{x^2+y^2}=1') -------------------------------------------------------------------------------- /01-data.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | 5 | 6 | st.title('데이터프레임 튜토리얼') 7 | 8 | # DataFrame 생성 9 | dataframe = pd.DataFrame({ 10 | 'first column': [1, 2, 3, 4], 11 | 'second column': [10, 20, 30, 40], 12 | }) 13 | 14 | # DataFrame 15 | # use_container_width 기능은 데이터프레임을 컨테이너 크기에 확장할 때 사용합니다. (True/False) 16 | st.dataframe(dataframe, use_container_width=False) 17 | 18 | 19 | # 테이블(static) 20 | # DataFrame과는 다르게 interactive 한 UI 를 제공하지 않습니다. 21 | st.table(dataframe) 22 | 23 | 24 | # # 메트릭 25 | st.metric(label="온도", value="10°C", delta="1.2°C") 26 | st.metric(label="삼성전자", value="61,000 원", delta="-1,200 원") 27 | 28 | # 컬럼으로 영역을 나누어 표기한 경우 29 | col1, col2, col3 = st.columns(3) 30 | col1.metric(label="달러USD", value="1,228 원", delta="-12.00 원") 31 | col2.metric(label="일본JPY(100엔)", value="958.63 원", delta="-7.44 원") 32 | col3.metric(label="유럽연합EUR", value="1,335.82 원", delta="11.44 원") -------------------------------------------------------------------------------- /02-basic-ui.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | from datetime import datetime as dt 4 | import datetime 5 | 6 | # 버튼 클릭 7 | button = st.button('버튼을 눌러보세요') 8 | 9 | if button: 10 | st.write(':blue[버튼]이 눌렸습니다 :sparkles:') 11 | 12 | 13 | # 파일 다운로드 버튼 14 | # 샘플 데이터 생성 15 | dataframe = pd.DataFrame({ 16 | 'first column': [1, 2, 3, 4], 17 | 'second column': [10, 20, 30, 40], 18 | }) 19 | 20 | # 다운로드 버튼 연결 21 | st.download_button( 22 | label='CSV로 다운로드', 23 | data=dataframe.to_csv(), 24 | file_name='sample.csv', 25 | mime='text/csv' 26 | ) 27 | 28 | # 체크 박스 29 | agree = st.checkbox('동의 하십니까?') 30 | 31 | if agree: 32 | st.write('동의 해주셔서 감사합니다 :100:') 33 | 34 | # 라디오 선택 버튼 35 | mbti = st.radio( 36 | '당신의 MBTI는 무엇입니까?', 37 | ('ISTJ', 'ENFP', '선택지 없음')) 38 | 39 | if mbti == 'ISTJ': 40 | st.write('당신은 :blue[현실주의자] 이시네요') 41 | elif mbti == 'ENFP': 42 | st.write('당신은 :green[활동가] 이시네요') 43 | else: 44 | st.write("당신에 대해 :red[알고 싶어요]:grey_exclamation:") 45 | 46 | # 선택 박스 47 | mbti = st.selectbox( 48 | '당신의 MBTI는 무엇입니까?', 49 | ('ISTJ', 'ENFP', '선택지 없음'), 50 | index=2 51 | ) 52 | 53 | if mbti == 'ISTJ': 54 | st.write('당신은 :blue[현실주의자] 이시네요') 55 | elif mbti == 'ENFP': 56 | st.write('당신은 :green[활동가] 이시네요') 57 | else: 58 | st.write("당신에 대해 :red[알고 싶어요]:grey_exclamation:") 59 | 60 | # 다중 선택 박스 61 | options = st.multiselect( 62 | '당신이 좋아하는 과일은 뭔가요?', 63 | ['망고', '오렌지', '사과', '바나나'], 64 | ['망고', '오렌지']) 65 | 66 | st.write(f'당신의 선택은: :red[{options}] 입니다.') 67 | 68 | 69 | # 슬라이더 70 | values = st.slider( 71 | '범위의 값을 다음과 같이 지정할 수 있어요:sparkles:', 72 | 0.0, 100.0, (25.0, 75.0)) 73 | st.write('선택 범위:', values) 74 | 75 | start_time = st.slider( 76 | "언제 약속을 잡는 것이 좋을까요?", 77 | min_value=dt(2020, 1, 1, 0, 0), 78 | max_value=dt(2020, 1, 7, 23, 0), 79 | value=dt(2020, 1, 3, 12, 0), 80 | step=datetime.timedelta(hours=1), 81 | format="MM/DD/YY - HH:mm") 82 | st.write("선택한 약속 시간:", start_time) 83 | 84 | 85 | # 텍스트 입력 86 | title = st.text_input( 87 | label='가고 싶은 여행지가 있나요?', 88 | placeholder='여행지를 입력해 주세요' 89 | ) 90 | st.write(f'당신이 선택한 여행지: :violet[{title}]') 91 | 92 | # 숫자 입력 93 | number = st.number_input( 94 | label='나이를 입력해 주세요.', 95 | min_value=10, 96 | max_value=100, 97 | value=30, 98 | step=5 99 | ) 100 | st.write('당신이 입력하신 나이는: ', number) 101 | -------------------------------------------------------------------------------- /03-lotto.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import random 3 | import datetime 4 | 5 | st.title(':sparkles:로또 생성기:sparkles:') 6 | 7 | 8 | def generate_lotto(): 9 | lotto = set() 10 | 11 | while len(lotto) < 6: 12 | number = random.randint(1, 46) 13 | lotto.add(number) 14 | 15 | lotto = list(lotto) 16 | lotto.sort() 17 | return lotto 18 | 19 | # st.subheader(f'행운의 번호: :green[{generate_lotto()}]') 20 | # st.write(f"생성된 시각: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}") 21 | 22 | button = st.button('로또를 생성해 주세요!') 23 | 24 | if button: 25 | for i in range(1, 6): 26 | st.subheader(f'{i}. 행운의 번호: :green[{generate_lotto()}]') 27 | st.write(f"생성된 시각: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}") 28 | 29 | -------------------------------------------------------------------------------- /04-chart.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | import seaborn as sns 5 | import numpy as np 6 | 7 | # 한글 폰트 설정 8 | plt.rcParams['font.family'] = "AppleGothic" 9 | # Windows, 리눅스 사용자 10 | # plt.rcParams['font.family'] = "NanumGothic" 11 | plt.rcParams['axes.unicode_minus'] = False 12 | 13 | 14 | # DataFrame 생성 15 | data = pd.DataFrame({ 16 | '이름': ['영식', '철수', '영희'], 17 | '나이': [22, 31, 25], 18 | '몸무게': [75.5, 80.2, 55.1] 19 | }) 20 | 21 | st.dataframe(data, use_container_width=True) 22 | 23 | fig, ax = plt.subplots() 24 | ax.bar(data['이름'], data['나이']) 25 | st.pyplot(fig) 26 | 27 | barplot = sns.barplot(x='이름', y='나이', data=data, ax=ax, palette='Set2') 28 | fig = barplot.get_figure() 29 | 30 | st.pyplot(fig) 31 | 32 | ############# 33 | 34 | labels = ['G1', 'G2', 'G3', 'G4', 'G5'] 35 | men_means = [20, 35, 30, 35, 27] 36 | women_means = [25, 32, 34, 20, 25] 37 | men_std = [2, 3, 4, 1, 2] 38 | women_std = [3, 5, 2, 3, 3] 39 | width = 0.35 # the width of the bars: can also be len(x) sequence 40 | 41 | fig, ax = plt.subplots() 42 | 43 | ax.bar(labels, men_means, width, yerr=men_std, label='Men') 44 | ax.bar(labels, women_means, width, yerr=women_std, bottom=men_means, 45 | label='Women') 46 | 47 | ax.set_ylabel('Scores') 48 | ax.set_title('Scores by group and gender') 49 | ax.legend() 50 | 51 | st.pyplot(fig) 52 | 53 | ##### Barcode 54 | 55 | code = np.array([ 56 | 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 57 | 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 58 | 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 59 | 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1]) 60 | 61 | pixel_per_bar = 4 62 | dpi = 100 63 | 64 | fig = plt.figure(figsize=(len(code) * pixel_per_bar / dpi, 2), dpi=dpi) 65 | ax = fig.add_axes([0, 0, 1, 1]) # span the whole figure 66 | ax.set_axis_off() 67 | ax.imshow(code.reshape(1, -1), cmap='binary', aspect='auto', 68 | interpolation='nearest') 69 | 70 | st.pyplot(fig) -------------------------------------------------------------------------------- /05-file.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import time 4 | 5 | # 파일 업로드 버튼 (업로드 기능) 6 | file = st.file_uploader("파일 선택(csv or excel)", type=['csv', 'xls', 'xlsx']) 7 | 8 | # 파일이 정상 업로드 된 경우 9 | # if file is not None: 10 | # # 파일 읽기 11 | # df = pd.read_csv(file) 12 | # # 출력 13 | # st.dataframe(df) 14 | 15 | time.sleep(3) 16 | 17 | # Excel or CSV 확장자를 구분하여 출력하는 경우 18 | if file is not None: 19 | ext = file.name.split('.')[-1] 20 | if ext == 'csv': 21 | # 파일 읽기 22 | df = pd.read_csv(file) 23 | # 출력 24 | st.dataframe(df) 25 | elif 'xls' in ext: 26 | # 엑셀 로드 27 | df = pd.read_excel(file, engine='openpyxl') 28 | # 출력 29 | st.dataframe(df) -------------------------------------------------------------------------------- /06-stock-chart.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import FinanceDataReader as fdr 3 | import datetime 4 | 5 | # Finance Data Reader 6 | # https://github.com/financedata-org/FinanceDataReader 7 | 8 | date = st.date_input( 9 | "조회 시작일을 선택해 주세요", 10 | datetime.datetime(2022, 1, 1) 11 | ) 12 | 13 | code = st.text_input( 14 | '종목코드', 15 | value='', 16 | placeholder='종목코드를 입력해 주세요' 17 | ) 18 | 19 | if code and date: 20 | df = fdr.DataReader(code, date) 21 | data = df.sort_index(ascending=True).loc[:, 'Close'] 22 | st.line_chart(data) -------------------------------------------------------------------------------- /07-stock-chart-2.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import FinanceDataReader as fdr 3 | import datetime 4 | import time 5 | 6 | # Finance Data Reader 7 | # https://github.com/financedata-org/FinanceDataReader 8 | 9 | st.title('종목 차트 검색') 10 | 11 | with st.sidebar: 12 | date = st.date_input( 13 | "조회 시작일을 선택해 주세요", 14 | datetime.datetime(2022, 1, 1) 15 | ) 16 | 17 | code = st.text_input( 18 | '종목코드', 19 | value='', 20 | placeholder='종목코드를 입력해 주세요' 21 | ) 22 | 23 | if code and date: 24 | df = fdr.DataReader(code, date) 25 | data = df.sort_index(ascending=True).loc[:, 'Close'] 26 | 27 | tab1, tab2 = st.tabs(['차트', '데이터']) 28 | 29 | with tab1: 30 | st.line_chart(data) 31 | 32 | with tab2: 33 | st.dataframe(df.sort_index(ascending=False)) 34 | 35 | with st.expander('컬럼 설명'): 36 | st.markdown(''' 37 | - Open: 시가 38 | - High: 고가 39 | - Low: 저가 40 | - Close: 종가 41 | - Adj Close: 수정 종가 42 | - Volumn: 거래량 43 | ''') 44 | -------------------------------------------------------------------------------- /08-mbti.py: -------------------------------------------------------------------------------- 1 | import json 2 | import configparser 3 | import http.client 4 | import streamlit as st 5 | 6 | class CompletionExecutor: 7 | def __init__(self, host, api_key, api_key_primary_val, request_id): 8 | self._host = host 9 | self._api_key = api_key 10 | self._api_key_primary_val = api_key_primary_val 11 | self._request_id = request_id 12 | 13 | def _send_request(self, completion_request): 14 | headers = { 15 | 'Content-Type': 'application/json; charset=utf-8', 16 | 'X-NCP-CLOVASTUDIO-API-KEY': self._api_key, 17 | 'X-NCP-APIGW-API-KEY': self._api_key_primary_val, 18 | 'X-NCP-CLOVASTUDIO-REQUEST-ID': self._request_id 19 | } 20 | 21 | conn = http.client.HTTPSConnection(self._host) 22 | conn.request('POST', '/testapp/v1/completions/LK-D', json.dumps(completion_request), headers) 23 | response = conn.getresponse() 24 | result = json.loads(response.read().decode(encoding='utf-8')) 25 | conn.close() 26 | return result 27 | 28 | def execute(self, completion_request): 29 | res = self._send_request(completion_request) 30 | if res['status']['code'] == '20000': 31 | return res['result']['text'] 32 | else: 33 | return 'Error' 34 | 35 | config = configparser.ConfigParser() 36 | config.sections() 37 | config.read('./your_apikey.ini') 38 | 39 | completion_executor = CompletionExecutor( 40 | host=config['CLOVA']['host'], 41 | api_key=config['CLOVA']['api_key'], 42 | api_key_primary_val=config['CLOVA']['api_key_primary_val'], 43 | request_id=config['CLOVA']['request_id'] 44 | ) 45 | 46 | st.title('MBTI 대백과사전') 47 | question = st.text_input( 48 | '질문', 49 | placeholder='질문을 입력해 주세요' 50 | ) 51 | 52 | if question: 53 | preset_text = f'MBTI에 대한 지식을 기반으로, 아래의 질문에 답해보세요.\n\n질문:{question}' 54 | 55 | request_data = { 56 | 'text': preset_text, 57 | 'maxTokens': 100, 58 | 'temperature': 0.5, 59 | 'topK': 0, 60 | 'topP': 0.8, 61 | 'repeatPenalty': 5.0, 62 | 'start': '\n$$$답:', 63 | 'stopBefore': ['###', '질문:', '답:', '###\n'], 64 | 'includeTokens': True, 65 | 'includeAiFilters': True, 66 | 'includeProbs': True 67 | } 68 | 69 | response_text = completion_executor.execute(request_data) 70 | st.markdown(response_text.split('$$$')[1]) -------------------------------------------------------------------------------- /09-clova.py: -------------------------------------------------------------------------------- 1 | import json 2 | import configparser 3 | import http.client 4 | import streamlit as st 5 | 6 | 7 | class CompletionExecutor: 8 | def __init__(self, host, api_key, api_key_primary_val, request_id): 9 | self._host = host 10 | self._api_key = api_key 11 | self._api_key_primary_val = api_key_primary_val 12 | self._request_id = request_id 13 | 14 | def _send_request(self, completion_request): 15 | headers = { 16 | 'Content-Type': 'application/json; charset=utf-8', 17 | 'X-NCP-CLOVASTUDIO-API-KEY': self._api_key, 18 | 'X-NCP-APIGW-API-KEY': self._api_key_primary_val, 19 | 'X-NCP-CLOVASTUDIO-REQUEST-ID': self._request_id 20 | } 21 | 22 | conn = http.client.HTTPSConnection(self._host) 23 | conn.request('POST', '/testapp/v1/completions/LK-D', json.dumps(completion_request), headers) 24 | response = conn.getresponse() 25 | result = json.loads(response.read().decode(encoding='utf-8')) 26 | conn.close() 27 | return result 28 | 29 | def execute(self, completion_request): 30 | res = self._send_request(completion_request) 31 | if res['status']['code'] == '20000': 32 | return res['result']['text'] 33 | else: 34 | return 'Error' 35 | 36 | 37 | config = configparser.ConfigParser() 38 | config.sections() 39 | config.read('./your_apikey.ini') 40 | 41 | completion_executor = CompletionExecutor( 42 | host=config['CLOVA']['host'], 43 | api_key=config['CLOVA']['api_key'], 44 | api_key_primary_val=config['CLOVA']['api_key_primary_val'], 45 | request_id=config['CLOVA']['request_id'] 46 | ) 47 | 48 | st.title('나만의 챗봇') 49 | 50 | preset_input = st.selectbox( 51 | '사전 문장', 52 | ('MBTI에 대한 지식을 기반으로, 아래의 질문에 답해보세요.', 53 | '키워드를 포함하여 설날 인사말을 생성합니다.', 54 | '30대 남성으로 질문에 군인말투로 끝을 다,나,까로 대답한다.', 55 | ), 56 | index=1 57 | ) 58 | 59 | question = st.text_area( 60 | '질문', 61 | placeholder='질문을 입력해 주세요', 62 | ) 63 | 64 | if preset_input and question: 65 | preset_text = f'{preset_input}\n\n질문:{question}' 66 | 67 | request_data = { 68 | 'text': preset_text, 69 | 'maxTokens': 100, 70 | 'temperature': 0.5, 71 | 'topK': 0, 72 | 'topP': 0.8, 73 | 'repeatPenalty': 5.0, 74 | 'start': '\n###답:', 75 | 'stopBefore': ['###', '질문:', '답:', '###\n'], 76 | 'includeTokens': True, 77 | 'includeAiFilters': True, 78 | 'includeProbs': True 79 | } 80 | 81 | response_text = completion_executor.execute(request_data) 82 | # print(preset_text) 83 | print(response_text) 84 | st.markdown(response_text.split('###')[1]) -------------------------------------------------------------------------------- /10-bitly-shorten.py: -------------------------------------------------------------------------------- 1 | import bitlyshortener 2 | import configparser 3 | import streamlit as st 4 | 5 | config = configparser.ConfigParser() 6 | config.sections() 7 | config.read('./your_apikey.ini') 8 | 9 | access_tokens = [config['bitly']['access_token']] 10 | shortener = bitlyshortener.Shortener(tokens=access_tokens) 11 | 12 | url = st.text_input('URL을 입력해 주세요') 13 | 14 | if url: 15 | shortend = shortener.shorten_urls([url]) 16 | st.markdown(f''' 17 | ### URL이 생성되었습니다:sparkles: 18 | 19 | **긴 주소** 20 | ''') 21 | st.code(f'{url}') 22 | st.markdown(f'**짧은 주소**') 23 | st.code(f'{shortend[0]}') -------------------------------------------------------------------------------- /11-national-pension.py: -------------------------------------------------------------------------------- 1 | import os 2 | import warnings 3 | import numpy as np 4 | import pandas as pd 5 | import matplotlib.pyplot as plt 6 | import seaborn as sns 7 | import re 8 | import streamlit as st 9 | 10 | # 한글 폰트 설정 11 | plt.rcParams['font.family'] = "AppleGothic" 12 | # Windows, 리눅스 사용자 13 | # plt.rcParams['font.family'] = "NanumGothic" 14 | plt.rcParams['axes.unicode_minus'] = False 15 | 16 | class PensionData(): 17 | def __init__(self, filepath): 18 | self.df = pd.read_csv(os.path.join(filepath), encoding='cp949') 19 | self.pattern1 = '(\([^)]+\))' 20 | self.pattern2 = '(\[[^)]+\])' 21 | self.pattern3 = '[^A-Za-z0-9가-힣]' 22 | self.preprocess() 23 | 24 | def preprocess(self): 25 | self.df.columns = [ 26 | '자료생성년월', '사업장명', '사업자등록번호', '가입상태', '우편번호', 27 | '사업장지번상세주소', '주소', '고객법정동주소코드', '고객행정동주소코드', 28 | '시도코드', '시군구코드', '읍면동코드', 29 | '사업장형태구분코드 1 법인 2 개인', '업종코드', '업종코드명', 30 | '적용일자', '재등록일자', '탈퇴일자', 31 | '가입자수', '금액', '신규', '상실' 32 | ] 33 | df = self.df.drop(['자료생성년월', '우편번호', '사업장지번상세주소', '고객법정동주소코드', '고객행정동주소코드', '사업장형태구분코드 1 법인 2 개인', '적용일자', '재등록일자'], axis=1) 34 | df['사업장명'] = df['사업장명'].apply(self.preprocessing) 35 | df['탈퇴일자_연도'] = pd.to_datetime(df['탈퇴일자']).dt.year 36 | df['탈퇴일자_월'] = pd.to_datetime(df['탈퇴일자']).dt.month 37 | df['시도'] = df['주소'].str.split(' ').str[0] 38 | df = df.loc[df['가입상태'] == 1].drop(['가입상태', '탈퇴일자'], axis=1).reset_index(drop=True) 39 | df['인당금액'] = df['금액'] / df['가입자수'] 40 | df['월급여추정'] = df['인당금액'] / 9 * 100 41 | df['연간급여추정'] = df['월급여추정'] * 12 42 | self.df = df 43 | 44 | 45 | def preprocessing(self, x): 46 | x = re.sub(self.pattern1, '', x) 47 | x = re.sub(self.pattern2, '', x) 48 | x = re.sub(self.pattern3, ' ', x) 49 | x = re.sub(' +', ' ', x) 50 | return x 51 | 52 | def find_company(self, company_name): 53 | return self.df.loc[self.df['사업장명'].str.contains(company_name), ['사업장명', '월급여추정', '연간급여추정', '업종코드', '가입자수']]\ 54 | .sort_values('가입자수', ascending=False) 55 | 56 | def compare_company(self, company_name): 57 | company = self.find_company(company_name) 58 | code = company['업종코드'].iloc[0] 59 | df1 = self.df.loc[self.df['업종코드'] == code, ['월급여추정', '연간급여추정']].agg(['mean', 'count', 'min', 'max']) 60 | df1.columns = ['업종_월급여추정', '업종_연간급여추정'] 61 | df1 = df1.T 62 | df1.columns = ['평균', '개수', '최소', '최대'] 63 | df1.loc['업종_월급여추정', company_name] = company['월급여추정'].values[0] 64 | df1.loc['업종_연간급여추정', company_name] = company['연간급여추정'].values[0] 65 | return df1 66 | 67 | def company_info(self, company_name): 68 | company = self.find_company(company_name) 69 | return self.df.loc[company.iloc[0].name] 70 | 71 | def get_data(self): 72 | return self.df 73 | 74 | @st.cache 75 | def read_pensiondata(): 76 | data = PensionData('https://www.dropbox.com/s/nxeo1tziv05ejz7/national-pension.csv?dl=1') 77 | return data 78 | 79 | data = read_pensiondata() 80 | company_name = st.text_input('회사명을 입력해 주세요', placeholder='검색할 회사명 입력') 81 | 82 | if data and company_name: 83 | output = data.find_company(company_name=company_name) 84 | if len(output) > 0: 85 | st.subheader(output.iloc[0]['사업장명']) 86 | info = data.company_info(company_name=company_name) 87 | st.markdown( 88 | f""" 89 | - `{info['주소']}` 90 | - 업종코드명 `{info['업종코드명']}` 91 | - 총 근무자 `{int(info['가입자수']):,}` 명 92 | - 신규 입사자 `{info['신규']:,}` 명 93 | - 퇴사자 `{info['상실']:,}` 명 94 | """ 95 | ) 96 | col1, col2, col3 = st.columns(3) 97 | col1.text('월급여 추정') 98 | col1.markdown(f"`{int(output.iloc[0]['월급여추정']):,}` 원") 99 | 100 | col2.text('연봉 추정') 101 | col2.markdown(f"`{int(output.iloc[0]['연간급여추정']):,}` 원") 102 | 103 | col3.text('가입자수 추정') 104 | col3.markdown(f"`{int(output.iloc[0]['가입자수']):,}` 명") 105 | 106 | st.dataframe(output.round(0), use_container_width=True) 107 | 108 | comp_output = data.compare_company(company_name=company_name) 109 | st.dataframe(comp_output.round(0), use_container_width=True) 110 | 111 | st.markdown(f'### 업종 평균 VS {company_name} 비교') 112 | 113 | percent_value = info['월급여추정'] / comp_output.iloc[0, 0] * 100 - 100 114 | diff_month = abs(comp_output.iloc[0, 0] - info['월급여추정']) 115 | diff_year = abs(comp_output.iloc[1, 0] - info['연간급여추정']) 116 | upordown = '높은' if percent_value > 0 else '낮은' 117 | 118 | st.markdown(f""" 119 | - 업종 **평균 월급여**는 `{int(comp_output.iloc[0, 0]):,}` 원, **평균 연봉**은 `{int(comp_output.iloc[1, 0]):,}` 원 입니다. 120 | - `{company_name}`는 평균 보다 `{int(diff_month):,}` 원, :red[약 {percent_value:.2f} %] `{upordown}` `{int(info['월급여추정']):,}` 원을 **월 평균 급여**를 받는 것으로 추정합니다. 121 | - `{company_name}`는 평균 보다 `{int(diff_year):,}` 원 `{upordown}` `{int(info['연간급여추정']):,}` 원을 **연봉**을 받는 것으로 추정합니다. 122 | """) 123 | 124 | fig, ax = plt.subplots(1, 2) 125 | 126 | p1 = ax[0].bar(x=["Average", "Your Company"], height=(comp_output.iloc[0, 0], info['월급여추정']), width=0.7) 127 | ax[0].bar_label(p1, fmt='%d') 128 | p1[0].set_color('black') 129 | p1[1].set_color('red') 130 | ax[0].set_title('Monthly Salary') 131 | 132 | p2 = ax[1].bar(x=["Average", "Your Company"], height=(comp_output.iloc[1, 0], info['연간급여추정']), width=0.7) 133 | p2[0].set_color('black') 134 | p2[1].set_color('red') 135 | ax[1].bar_label(p2, fmt='%d') 136 | ax[1].set_title('Yearly Salary') 137 | 138 | ax[0].tick_params(axis='both', which='major', labelsize=8, rotation=0) 139 | ax[0].tick_params(axis='both', which='minor', labelsize=6) 140 | ax[1].tick_params(axis='both', which='major', labelsize=8) 141 | ax[1].tick_params(axis='both', which='minor', labelsize=6) 142 | 143 | st.pyplot(fig) 144 | 145 | st.markdown('### 동종업계') 146 | df = data.get_data() 147 | st.dataframe(df.loc[df['업종코드'] == info['업종코드'], ['사업장명', '월급여추정', '연간급여추정', '가입자수']]\ 148 | .sort_values('연간급여추정', ascending=False).head(10).round(0), 149 | use_container_width=True 150 | ) 151 | 152 | else: 153 | st.subheader('검색결과가 없습니다') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Streamlit 튜토리얼 😁 2 | 3 | `Streamlit`을 활용하여 파이썬 코드로 빠르게 웹앱(WebApp)을 생성할 수 있습니다. 4 | 사용법도 굉장히 쉽고, 무료 호스팅 기능도 지원해주기 때문에, 데모 앱을 만들어 볼 때 굉장히 유용합니다. 5 | 6 | **설치** 7 | ```python 8 | pip install streamlit 9 | ``` 10 | 11 | - 실습파일: https://github.com/teddylee777/streamlit-tutorial 12 | - 도큐먼트: https://docs.streamlit.io/ 13 | 14 | ## YouTube 튜토리얼 영상 15 | 16 | - [EP01. Streamlit으로 빠르게 웹앱 생성하기 - 설치, 환경설정, 실습파일](https://www.youtube.com/watch?v=Gr5Vuo7TCaE) 17 | - [EP02. 텍스트 컴포넌트](https://youtu.be/CiOfNvp-KmA) 18 | - [EP03. 데이터프레임(DataFrame), 테이블 출력](https://youtu.be/C73XAQJFa1E) 19 | - [EP04. 자주 사용하는 위젯(Widgets)들](https://youtu.be/3CWpFR-EkQc) 20 | - [EP05. 로또 생성기 웹사이트 만들기](https://youtu.be/2mER-EvDWzo) 21 | - [EP06. 그래프, 차트 그리기](https://youtu.be/2424N7ITZvo) 22 | - [EP07. 파일 업로드 기능 & 업로드된 파일 처리](https://youtu.be/L3ExMrinu20) 23 | - [EP08. 주식 데이터 조회, 주가 차트 그리기](https://youtu.be/0PA3XsPwPDg) 24 | - [EP09. 사이드바, 탭 추가하기](https://youtu.be/frdggOd5eNQ) 25 | - [EP10. MBTI 대백과사전 웹앱 만들기 (NAVER Clova API 활용)](https://youtu.be/2tT8-peVTLw) 26 | - [EP11. Naver Clova Studio API를 활용한 챗봇 서비스 만들기](https://youtu.be/sLTe2jMGdYU) 27 | - [EP12. 기업의 평균 월급여, 연봉 추정하기 (국민연금 데이터 활용)](https://youtu.be/-CMZKaoX5Og) 28 | - [EP13. 기업의 월급여, 연봉 추정 서비스 제작 & 웹으로 배포 (무료 호스팅)](https://youtu.be/OpeECxk5c-Q) 29 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altair==4.2.0 2 | attrs==22.2.0 3 | backports.zoneinfo==0.2.1 4 | beautifulsoup4==4.11.1 5 | bitlyshortener==0.6.4 6 | blinker==1.5 7 | cachetools==5.2.1 8 | charset-normalizer==3.0.1 9 | click==8.1.3 10 | commonmark==0.9.1 11 | contourpy==1.0.7 12 | cycler==0.11.0 13 | decorator==5.1.1 14 | entrypoints==0.4 15 | et-xmlfile==1.1.0 16 | finance-datareader==0.9.50 17 | fonttools==4.38.0 18 | gitdb==4.0.10 19 | GitPython==3.1.30 20 | idna==3.4 21 | importlib-metadata==6.0.0 22 | importlib-resources==5.10.2 23 | Jinja2==3.1.2 24 | jsonschema==4.17.3 25 | kiwisolver==1.4.4 26 | lxml==4.9.2 27 | MarkupSafe==2.1.2 28 | matplotlib==3.6.3 29 | numpy==1.24.1 30 | openpyxl==3.0.10 31 | packaging==23.0 32 | pandas==1.5.3 33 | Pillow==9.4.0 34 | pkgutil_resolve_name==1.3.10 35 | plotly==5.12.0 36 | protobuf==3.20.3 37 | pyarrow==10.0.1 38 | pydeck==0.8.0 39 | Pygments==2.14.0 40 | Pympler==1.0.1 41 | pyparsing==3.0.9 42 | pyrsistent==0.19.3 43 | python-dateutil==2.8.2 44 | pytz==2022.7.1 45 | pytz-deprecation-shim==0.1.0.post0 46 | requests==2.28.2 47 | requests-file==1.5.1 48 | rich==13.1.0 49 | seaborn==0.12.2 50 | semver==2.13.0 51 | six==1.16.0 52 | smmap==5.0.0 53 | soupsieve==2.3.2.post1 54 | streamlit==1.17.0 55 | tenacity==8.1.0 56 | toml==0.10.2 57 | toolz==0.12.0 58 | tornado==6.2 59 | tqdm==4.64.1 60 | typing_extensions==4.4.0 61 | tzdata==2022.7 62 | tzlocal==4.2 63 | urllib3==1.26.14 64 | validators==0.20.0 65 | watchdog==2.2.1 66 | zipp==3.11.0 67 | -------------------------------------------------------------------------------- /your_apikey.ini: -------------------------------------------------------------------------------- 1 | [CLOVA] 2 | host= 3 | api_key= 4 | api_key_primary_val= 5 | request_id= 6 | 7 | [bitly] 8 | access_token= 9 | --------------------------------------------------------------------------------