<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Kingjo's blog</title>
        <link>https://velog.io/</link>
        <description>나사 빠진 걸 좋아합니다</description>
        <lastBuildDate>Wed, 14 Jun 2023 10:47:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Kingjo's blog</title>
            <url>https://velog.velcdn.com/images/kingjo_/profile/54acaf10-c7a6-4dbc-871e-36e343e461ac/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Kingjo's blog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kingjo_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[추억 점수]]></title>
            <link>https://velog.io/@kingjo_/%EC%B6%94%EC%96%B5-%EC%A0%90%EC%88%98</link>
            <guid>https://velog.io/@kingjo_/%EC%B6%94%EC%96%B5-%EC%A0%90%EC%88%98</guid>
            <pubDate>Wed, 14 Jun 2023 10:47:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kingjo_/post/0cb0a1bf-348a-4e98-96db-41dbccd418fe/image.png" alt=""></p>
<h3 id="내-풀이">내 풀이</h3>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/9049b663-0282-442c-9f80-a3406cd5b5b4/image.png" alt=""></p>
<p>zip만 쓸 줄 알면 간단하게 풀 수 있는 문제였다. 다른 사람 풀이를 보니까, 한 줄로 끝내는 코드도 있던데... 신기하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Http 메소드 정리]]></title>
            <link>https://velog.io/@kingjo_/Http-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@kingjo_/Http-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 13 Jun 2023 02:32:40 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kingjo_/post/e3482437-0733-49de-81ed-052da2772e96/image.png" alt=""></p>
<p>GET:
GET 메소드는 서버로부터 특정 리소스(웹 페이지, 이미지, 동영상 등)를 요청할 때 사용된다. 요청된 리소스는 서버에서 응답으로 전송되어 클라이언트에게 보여진다. GET 메소드는 서버의 데이터를 읽을 때만 사용되며, 요청 파라미터는 URL에 포함된다.</p>
<p>HEAD:
HEAD 메소드는 GET 메소드와 유사하지만, 서버에서는 응답 본문을 반환하지 않고 응답 헤더만 반환한다. 주로 특정 리소스의 메타데이터(크기, 수정일 등)를 확인하기 위해 사용된다.</p>
<p>PUT:
PUT 메소드는 서버에 새로운 리소스를 생성하거나 기존 리소스를 업데이트할 때 사용된다. 클라이언트는 리소스의 전체 내용을 요청 본문에 담아서 서버에 보낸다. 만약 기존에 같은 리소스가 있다면 업데이트하고, 없다면 새로운 리소스를 생성한다.</p>
<p>POST:
POST 메소드는 클라이언트에서 서버로 데이터를 보낼 때 사용된다. 보통 HTML의 폼(form) 데이터를 전송할 때 사용되며, 요청된 데이터는 서버에서 처리하고 응답을 반환한다. POST 메소드는 데이터를 전송하는 용도로 주로 사용되며, 요청 파라미터는 요청 본문(body)에 포함된다.</p>
<p>PATCH:
PATCH 메소드는 서버에서 특정 리소스의 부분적인 업데이트를 요청할 때 사용된다. 클라이언트는 업데이트할 리소스의 식별자와 업데이트 내용을 요청 본문에 담아 PATCH 요청을 보낸다. 서버는 해당 리소스를 부분적으로 업데이트하고 성공 여부를 응답한다.</p>
<p>TRACE:
TRACE 메소드는 클라이언트가 서버에게 자신의 요청이 서버에 도달했을 때 어떻게 처리되는지 확인하기 위해 사용된다. 보안상의 문제로 잘 사용되지 않는다.</p>
<p>OPTIONS:
OPTIONS 메소드는 서버에서 지원하는 메소드들의 목록을 요청하는데 사용된다. 서버는 Allow 헤더를 통해 지원하는 메소드들을 응답으로 반환한다.</p>
<p>DELETE:
DELETE 메소드는 서버에서 특정 리소스를 삭제할 때 사용된다. 클라이언트는 삭제할 리소스의 식별자를 URL에 포함시켜 DELETE 요청을 보낸다. 서버는 해당 리소스를 삭제하고 성공 여부를 응답한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[달리기경주]]></title>
            <link>https://velog.io/@kingjo_/%EB%8B%AC%EB%A6%AC%EA%B8%B0%EA%B2%BD%EC%A3%BC</link>
            <guid>https://velog.io/@kingjo_/%EB%8B%AC%EB%A6%AC%EA%B8%B0%EA%B2%BD%EC%A3%BC</guid>
            <pubDate>Sun, 11 Jun 2023 21:53:45 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kingjo_/post/3e505cb4-dc5b-4162-b32c-04bab9bfd91d/image.png" alt=""></p>
<p>크게 어렵지 않은 문제인 것 같아서. list의 index를 활용한 풀이를 해보았다.</p>
<h3 id="내-풀이">내 풀이</h3>
<pre><code>def solution(players, callings):
    for calls in callings:
        calling_player = players.index(calls)
        players[calling_player], players[calling_player-1] = players[calling_player-1], players[calling_player] 
    answer = players

    return answer</code></pre><p><img src="https://velog.velcdn.com/images/kingjo_/post/89539f6d-9147-4b74-ba7c-c12945067a14/image.png" alt=""></p>
<p>로직은 맞는데, list.index()의 속도가 매우 느린 것 같다. (list의 처음부터 끝까지 순회하면서 parameter를 찾으니까...) 따라서 다른 방법을 찾아보았다.</p>
<pre><code>def solution(players, callings):    
    player_dict = {player:rank for rank, player in enumerate(players)}
    rank_dict = {rank:player for rank, player in enumerate(players)}

    for call in callings:
        rank = player_dict[call]

        player_dict[rank_dict[rank-1]], player_dict[rank_dict[rank]] = player_dict[rank_dict[rank]], player_dict[rank_dict[rank-1]]
        rank_dict[rank-1], rank_dict[rank] = rank_dict[rank], rank_dict[rank-1]

    return list(rank_dict.values())</code></pre><p>실시간 현황판처럼 딕셔너리를 만들어서 업데이트를 하는 방식으로 풀었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Coinprice Dashboard 중간 정산]]></title>
            <link>https://velog.io/@kingjo_/Coinprice-Dashboard-%EC%A4%91%EA%B0%84-%EC%A0%95%EC%82%B0</link>
            <guid>https://velog.io/@kingjo_/Coinprice-Dashboard-%EC%A4%91%EA%B0%84-%EC%A0%95%EC%82%B0</guid>
            <pubDate>Fri, 09 Jun 2023 01:52:42 GMT</pubDate>
            <description><![CDATA[<p>Github : <a href="https://github.com/ScrewlessKingjo/CoinPrice_Dashboard">https://github.com/ScrewlessKingjo/CoinPrice_Dashboard</a></p>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/71b06cbe-4589-4459-9deb-5ca804b273b8/image.gif" alt=""></p>
<p>Redis와 Streamlit을 이용해 사이드 프로젝트를 진행하였다. 프로젝트에 대한 개요는 Github에 나와있으니, 진행하며 생겼던 문제점, 앞으로의 보완 방향에 대해서 생각해보면 좋을 것 같다.</p>
<h2 id="library--framework">Library &amp; Framework</h2>
<h3 id="streamlit">Streamlit</h3>
<p> Streamlit은 파이썬으로 인터랙티브한 웹 애플리케이션을 빠르고 간편하게 구축할 수 있는 오픈 소스 라이브러리이다.</p>
<p>주요 특장점은 다음과 같다.</p>
<ol>
<li><p><strong>간단한 구조</strong>: Streamlit은 파이썬 스크립트에 직접 코드를 작성하므로 개발자가 추가 프레임워크나 구조에 대해 걱정할 필요가 없다. 간단하고 직관적인 구조로 빠르게 애플리케이션을 작성할 수 있다.</p>
</li>
<li><p><strong>실시간 업데이트</strong>: Streamlit 애플리케이션은 실시간으로 업데이트되며, 코드를 수정하면 자동으로 브라우저에서 변경 사항이 반영된다. 이를 통해 개발자는 빠른 피드백 루프를 유지하고 시각적인 결과를 실시간으로 확인할 수 있다.</p>
</li>
<li><p><strong>다양한 컴포넌트</strong>: Streamlit은 다양한 종류의 컴포넌트를 제공하여 텍스트, 이미지, 그래프, 테이블 등 다양한 형태의 데이터를 표시할 수 있다. 또한 사용자 입력을 받아 처리하는 버튼, 슬라이더, 체크박스 등의 컴포넌트도 제공한다.</p>
</li>
<li><p><strong>대화형 요소</strong>: Streamlit은 사용자와 상호작용할 수 있는 대화형 요소를 제공하여 사용자 입력에 따라 결과를 동적으로 업데이트할 수 있다. 사용자 입력을 통해 데이터를 필터링하거나 파라미터를 조정하여 결과를 실시간으로 확인할 수 있다.</p>
</li>
<li><p><strong>배포 및 공유</strong>: Streamlit으로 작성한 애플리케이션은 간단한 명령어로 로컬 머신에서 실행할 수 있을 뿐만 아니라, 클라우드 플랫폼에 배포하여 다른 사람과 공유할 수도 있다.</p>
</li>
</ol>
<p>전 직장에서 한번 사용해본 적이 있었는데, 프로젝트가 유야무야되서 사실 &#39;이런 게 있구나&#39; 정도만 경험했었던 것 같다. 데이터나 ML 프로젝트를 하면 이걸 어떤 애플리케이션에 띄울지도 고민 거리인데, 그런 부분에서 간편하게 빌드해서 대시보드를 만들 수 있다는 건 큰 장점인 것 같다.</p>
<p>다만 사용을 해보니 실시간 업데이트를 지원하지 않는 컴포넌트가 좀 많았던 것 같다... matplotlib이나 seaborn 같은 훌륭한 시각화 라이브러리에 익숙해져서 그런지, 컴포넌트에 대한 커스터마이징도 다소 제한적이었던 것 같다. (사실 streamlit 안에서도 이런 시각화 라이브러리들을 import 받아서 사용 가능하다) 그래도 디자인에 영 젬병인 나 같은 개발자도 &#39;그럴싸하게&#39; 대시보드를 만들 수 있다는 건 큰 장점인 것 같다. </p>
<h3 id="redis">Redis</h3>
<p>Redis는 인메모리 데이터 스토어로서 오픈 소스 기반의 키-값(key-value) 데이터베이스 시스템이다. Redis는 Remote Dictionary Server의 약자이며, 데이터를 메모리에 저장하고 조회하기 위한 빠른 읽기 및 쓰기 성능을 제공한다.</p>
<p>주요 특장점은 다음과 같다:</p>
<p>속도와 성능: Redis는 데이터를 메모리에 저장하고 처리하기 때문에 매우 빠른 읽기 및 쓰기 성능을 제공한다. 이를 통해 실시간 캐싱, 세션 관리, 메시지 큐 등 다양한 용도로 사용할 수 있다.</p>
<p>다양한 데이터 구조: Redis는 단순한 키-값 저장소뿐만 아니라 다양한 데이터 구조를 지원한다. 문자열, 해시, 리스트, 셋, 정렬된 셋 등의 데이터 구조를 활용하여 다양한 데이터 모델을 구축할 수 있다.</p>
<p>지속성: Redis는 데이터를 메모리에 저장하면서 동시에 디스크에도 저장할 수 있다. 이를 통해 데이터의 지속성을 보장하고 시스템 장애 시에도 데이터를 복구할 수 있다.</p>
<p>분산 시스템 지원: Redis는 클러스터링을 통해 데이터를 여러 노드에 분산하여 저장하고 처리할 수 있다. 이를 통해 확장성과 고가용성을 제공하며 대용량 데이터 처리에 유용하다.</p>
<p>NoSQL의 하위 분류라 할 수 있는 Key-Value 데이터베이스의 대표적인 DB. 특장점에서 언급하듯 인메모리에서 job을 처리하므로 매우 빠르게 데이터를 조회하거나 쓸 수 있다! 아직 더 써봐야 알겠지만, 일반적인 rdb에서 느낄 수 있는 속도와는 확연히 다른 모습을 보여준다. 또한 데이터 구조가 여러 가지가 있어 잘 활용할 수만 있다면 NoSQL을 사용할 때 항상 겪는 문제점인 조회 후 Parsing을 고려한 쓰기가 가능할 것 같다.</p>
<h2 id="개발-과정">개발 과정</h2>
<h3 id="데이터-적재">데이터 적재</h3>
<pre><code>def OrderbookDataCollector(url, headers,key_list):
    result_dict = {}
    response = requests.get(url, headers=headers)
    data = json.loads(response.text)[&#39;data&#39;]

    for key in key_list :
        bid_list = []
        asks_list = []

        for count in data[key][&#39;bids&#39;]:
            bid_list.append(float(count[&#39;price&#39;]))

        for count in data[key][&#39;asks&#39;]:
            asks_list.append(float(count[&#39;price&#39;]))

        result_dict[key] = {&#39;bids&#39; : int(np.round(sum(bid_list)/len(bid_list), 0)),
            &#39;asks&#39; : int(np.round(sum(asks_list)/len(asks_list), 0))}      


    return result_dict


def DataCollector(url, headers,key_list, rd) :

    result_dict = {}
    response = requests.get(url[&#39;ticker&#39;], headers=headers)
    data = json.loads(response.text)[&#39;data&#39;]
    unix_time = int(data[&#39;date&#39;]) / 1000
    datekey = datetime.datetime.fromtimestamp(unix_time).strftime(&#39;%Y-%m-%d %H:%M:%S&#39;)

    for key in key_list : 
        result_dict[key] = data[key]


    orderbook_data = OrderbookDataCollector(url[&#39;orderbook&#39;], headers, key_list)

    combined_dict = {
        currency: {
            **values,
            &#39;bids&#39;: orderbook_data[currency][&#39;bids&#39;],
            &#39;asks&#39;: orderbook_data[currency][&#39;asks&#39;]
        }
        for currency, values in result_dict.items()
    }
    jsonDataDict = json.dumps(combined_dict, ensure_ascii=False).encode(&#39;utf-8&#39;)
    rd.set(datekey, jsonDataDict)

    return None
</code></pre><p>두 가지의 API를 받아, 데이터를 원하는 형태로 변환하여 적재하기를 원하였으므로 두 개의 함수를 만들어 데이터 적재를 진행했다. Orderbook API에서 필요한건 매수가(bids)와 매도가(asks) 뿐이었으므로 두 key만 파싱하여 return하도록 하였고, DataCollector 함수에서 각 가상화폐 별로 매수가와 매도가를 추가한 뒤 Redis에 저장하였다.</p>
<p>&nbsp;</p>
<pre><code>def GetLatestData(redis_client, count):
    keys = redis_client.keys()
    sorted_keys = sorted(keys, reverse=True)  
    temp_dict = {}
    for key in sorted_keys[:count]:
        value = redis_client.get(key)
        temp_dict[key.decode()] = json.loads(value.decode())

    return temp_dict


def DataFrameGenerator(result, key_list):
    result_dict = {}

    for coin in key_list:
        df_list = []
        for dict_key in result.keys():
            df_list.append(result[dict_key][coin])
        df = pd.DataFrame(df_list, index=result.keys())
        result_dict[coin] = df

    return result_dict
</code></pre><p>레디스에서 데이터를 조회하여 가져오는 함수도 만들었다. 나는 Redis를 사용해본 적이 없어서, RDB처럼 그냥 order by로 최신 데이터를 가져와야겠다 생각을 하고 있었는데, 그런 기능을 사용하기 위해서는 Sorted Set이라는 데이터 구조를 활용해야 했다. 일단 나중에 개선하기로 하고, 일괄적으로 key값을 불러와 그 key를 정렬하여 최신 데이터를 가져왔다. 당연히 성능에 큰 제약이 되겠지만, 원체 속도가 빨라 아직까지는 느끼지 못했다.</p>
<p>GetLatestData 함수에서 일자 별로 데이터를 가져온 다음, 대시보드를 만들기 위해 DataFrameGenerator 함수에서 coin 별로 데이터를 추출해 딕셔너리에 담아 리턴하였다.</p>
<p>조회 함수를 재활용할 생각으로 함수를 분리해놨는데, 생각해보니 굳이 분리되어 있을 필요는 없는 것 같기도 하다.</p>
<p>&nbsp;</p>
<pre><code>def DataLoader(rd, data_count, KEY_LIST):
    data_list = GetLatestData(rd, data_count)
    df_dict = DataFrameGenerator(data_list, KEY_LIST)

    return df_dict</code></pre><p>일일이 치기 귀찮아서...매우 안좋은 방법이지만 두 함수를 호출하는 함수를 별도로 만들었다.</p>
<p>&nbsp;</p>
<pre><code>def ChartDataFilter(df):

    columns_keep = [&#39;min_price&#39;, &#39;max_price&#39;, &#39;bids&#39;, &#39;asks&#39;]
    columns_drop = [col for col in df.columns if col not in columns_keep]

    df_filtered = df.drop(columns_drop, axis=1)

    return df_filtered[::-1]


def TableDataFilter(df_dict) : 
    index = [&#39;시가&#39;, &#39;종가&#39;, &#39;저가&#39;, &#39;고가&#39;, &#39;거래량&#39;, &#39;거래금액&#39;, &#39;전일종가&#39;, &#39;최근 1일 거래량&#39;, &#39;최근 1일 거래금액&#39;, &#39;최근 1일 변동가&#39;, &#39;최근 1일 변동률&#39;, &#39;최신 매수가&#39;, &#39;최신 매도가&#39;]
    for coin in df_dict.keys():
        df = pd.DataFrame(df_dict[coin])
        df.index= index

    return df</code></pre><p>chart를 그리는데 사용되는 df로 가공하는 함수와, Table 만드는 df를 가공하는 함수를 별도로 만들었다. Chart에는 매수가와 매도가만 띄울 생각이어서 해당하는 컬럼만 추출했다. </p>
<pre><code>import time
import streamlit as st
import altair as alt
import pandas as pd
import redis
import DataHandler as DH


URL_DICT = {
    &#39;ticker&#39;: &#39;https://api.bithumb.com/public/ticker/ALL_KRW&#39;,
    &#39;orderbook&#39;: &#39;https://api.bithumb.com/public/orderbook/ALL_KRW&#39;
}
HEADERS = {&quot;accept&quot;: &quot;application/json&quot;}
KEY_LIST = [&#39;BTC&#39;, &#39;ETC&#39;, &#39;XRP&#39;, &#39;BCH&#39;, &#39;QTUM&#39;, &#39;BTG&#39;]

rd = redis.StrictRedis(host=&#39;localhost&#39;, port=6379, db=0)
data_count = 15


def Load(url, headers, _rd, data_count, KEY_LIST):
    DH.DataCollector(url, headers, KEY_LIST, _rd)
    dataset = DH.DataLoader(_rd, data_count, KEY_LIST)

    return dataset



def Line_Chart(data):
    chart_df = DH.ChartDataFilter(data)

    x = pd.Series(pd.to_datetime(chart_df.index)).dt.strftime(&#39;%H:%M:%S&#39;)

    bid_price = chart_df[&#39;bids&#39;]
    asks_price = chart_df[&#39;asks&#39;]

    data = pd.DataFrame({&#39;x&#39;: x, &#39;bids&#39;: list(bid_price), &#39;asks&#39;: list(asks_price)})

    bid_scale = alt.Scale(domain=(data[&#39;bids&#39;].min()*0.999995, data[&#39;bids&#39;].max()*1.000005))
    asks_scale = alt.Scale(domain=(data[&#39;asks&#39;].min()*0.999995, data[&#39;asks&#39;].max()*1.000005))

    bid_line = alt.Chart(data).mark_line(color=&#39;blue&#39;).encode(
        x=alt.X(&#39;x&#39;, axis=alt.Axis(title=&#39;시간&#39;)),
        y=alt.Y(&#39;bids&#39;, scale=bid_scale, axis=alt.Axis(title=&#39;가격&#39;)),
        tooltip=[&#39;x&#39;, &#39;bids&#39;])

    asks_line = alt.Chart(data).mark_line(color=&#39;red&#39;).encode(
        x=alt.X(&#39;x&#39;, axis=alt.Axis(title=&#39;시간&#39;)),
        y=alt.Y(&#39;asks&#39;, scale=asks_scale, axis=alt.Axis(title=&#39;가격&#39;)),
        tooltip=[&#39;x&#39;, &#39;asks&#39;])

    chart = alt.layer(bid_line, asks_line).resolve_scale(y=&#39;independent&#39;)

    return chart</code></pre><p>Chart를 생성하는 함수. altair를 Streamlit에서 호출하는 라이브러리를 이용해 차트를 만들었다. streamlit에도 자체적인 line chart가 있지만, 미관상 좋지 않을 뿐더러(테마를 config해주면 해결되는 문제이긴 했다) 실시간성으로 데이터를 업데이트 하는 기능이 부실했다. Community에도 altair를 활용하라는 이야기만 있어서, altair를 사용한 차트를 그렸다. </p>
<p>다만 여기에도 문제가 하나 있었는데, altair에는 범례를 나타내주는 기능이 없었다! 어차피 2개 line만 있으므로, 반드시 필요하지는 않을 것 같다고 생각해 더 깊게 파보지는 않았다.</p>
<pre><code>st.set_page_config(layout=&quot;wide&quot;)
st.title(&#39;가상화폐 시세 추이&#39;)

with st.container():
    table = st.empty()


dataset = Load(URL_DICT, HEADERS, rd, data_count, KEY_LIST)

col1, col2, col3 = st.columns(3)

with col1:
    st.subheader(&#39;BTC&#39;)
    BTC_Chart = Line_Chart(dataset[&#39;BTC&#39;])
    chart_component1 = st.altair_chart(BTC_Chart, use_container_width=True)

with col2:
    st.subheader(&#39;ETC&#39;)
    ETC_Chart = Line_Chart(dataset[&#39;ETC&#39;])
    chart_component2 = st.altair_chart(ETC_Chart, use_container_width=True)

with col3:
    st.subheader(&#39;XRP&#39;)
    XRP_Chart = Line_Chart(dataset[&#39;XRP&#39;])
    chart_component3 = st.altair_chart(XRP_Chart, use_container_width=True)

col4, col5, col6 = st.columns(3)

with col4:
    st.subheader(&#39;BCH&#39;)
    BCH_Chart = Line_Chart(dataset[&#39;BCH&#39;])
    chart_component4 = st.altair_chart(BCH_Chart, use_container_width=True)

with col5:
    st.subheader(&#39;QTUM&#39;)
    QTUM_Chart = Line_Chart(dataset[&#39;QTUM&#39;])
    chart_component5 = st.altair_chart(QTUM_Chart, use_container_width=True)

with col6:
    st.subheader(&#39;BTG&#39;)
    BTG_Chart = Line_Chart(dataset[&#39;BTG&#39;])
    chart_component6 = st.altair_chart(BTG_Chart, use_container_width=True)</code></pre><p>데이터를 불러오고, 웹에서 표시될 streamlit의 레이아웃을 설정하였다. 최 상단에 있는 st.empty()는 이후 테이블을 넣을 자리로, 후술할 While문에서 데이터를 채워넣기 위해 일단은 객체 선언만 해두었다. 그리고 아까 만든 Line_Chart 함수를 사용하여 각 컨테이너마다 Chart 객체를 선언하고, 초기에 불러온 데이터셋을 통해 라인 차트를 그렸다.</p>
<p>여기서 볼 수 있듯, Streamlit은 다른 웹 프레임워크 &amp; 시각화 라이브러리에 비해 간단하게 프론트 딴을 설정할 수 있어서 매우 좋다. 대신 지원하는 기능이 아직은 많지 않아, 더 복잡한 커스터마이징이 필요할 경우 다소 복잡해지는 문제점이 있다고 한다.</p>
<pre><code>while True:
    dataset = Load(URL_DICT, HEADERS, rd, data_count, KEY_LIST)

    chart_component1.altair_chart(Line_Chart(dataset[&#39;BTC&#39;]), use_container_width=True)
    chart_component2.altair_chart(Line_Chart(dataset[&#39;ETC&#39;]), use_container_width=True)
    chart_component3.altair_chart(Line_Chart(dataset[&#39;XRP&#39;]), use_container_width=True)
    chart_component4.altair_chart(Line_Chart(dataset[&#39;BCH&#39;]), use_container_width=True)
    chart_component5.altair_chart(Line_Chart(dataset[&#39;QTUM&#39;]), use_container_width=True)
    chart_component6.altair_chart(Line_Chart(dataset[&#39;BTG&#39;]), use_container_width=True)
    table_dataset  = DH.GetLatestData(rd, 1)
    table_data = DH.TableDataFilter(table_dataset)
    table.dataframe(table_data.astype(str), width=2200, height=512, use_container_width=True)
    time.sleep(3)</code></pre><p>맨 하단의 반복문에서 데이터를 주기적으로 Redis에서 불러오고, 이를 통해 각 개체를 업데이트 하는 방식으로 수정하였다. 테이블 안에 들어가는 데이터를 통으로 업데이트하는 방식이라, 사실 올바르게 구현된 방식은 아닌 것 같다. 3초마다 업데이트 될 수 있도록 time을 걸어놓았다.</p>
<h2 id="개선점">개선점</h2>
<p>앞서 언급하였듯 데이터의 I/O를 최대한 줄이는 방향으로 코드를 바꾸는게 첫 번째 과제인 것 같다. 두 번째로, 지금은 두 가지 정보만 단순하게 보여주고 있는 정도라 대시보드라 부르기 민망한 정도인 것 같다. 추가적으로 몇 가지 정보를 더 띄워주면 좋을 것 같은데, 아직까지는 어떤걸 띄워줘야 할지 감이 안잡힌다. 머신러닝으로 가격 예측을 해봐도 좋을 것 같고...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Coinprice Dashboard 기획]]></title>
            <link>https://velog.io/@kingjo_/%EC%82%AC%EC%9D%B4%EB%93%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%ED%9A%8D</link>
            <guid>https://velog.io/@kingjo_/%EC%82%AC%EC%9D%B4%EB%93%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%ED%9A%8D</guid>
            <pubDate>Wed, 07 Jun 2023 00:25:47 GMT</pubDate>
            <description><![CDATA[<h3 id="overview">Overview</h3>
<p>API를 통해 데이터를 수집한 후, 이를 In-memory db에 저장한 뒤 웹 애플리케이션으로 시각화하는 프로젝트를 기획했다. 실시간성으로 데이터 수집 - 적재 - 시각화가 이루어지면 괜찮을 것 같다.</p>
<p>API : </p>
<p><a href="https://apidocs.bithumb.com/reference/%ED%98%84%EC%9E%AC%EA%B0%80-%EC%A0%95%EB%B3%B4-%EC%A1%B0%ED%9A%8C-all">https://apidocs.bithumb.com/reference/%ED%98%84%EC%9E%AC%EA%B0%80-%EC%A0%95%EB%B3%B4-%EC%A1%B0%ED%9A%8C-all</a></p>
<p>스킬셋 : </p>
<p>Python
Redis(In memory DB)
Streamlit(시각화 프레임워크)</p>
<p>데이터 예시 :</p>
<pre><code>### 현재가 정보
{
  &quot;status&quot;: &quot;0000&quot;,
  &quot;data&quot;: {
    &quot;BTC&quot;: {
      &quot;opening_price&quot;: &quot;34499000&quot;,
      &quot;closing_price&quot;: &quot;35766000&quot;,
      &quot;min_price&quot;: &quot;34333000&quot;,
      &quot;max_price&quot;: &quot;35974000&quot;,
      &quot;units_traded&quot;: &quot;583.23470018&quot;,
      &quot;acc_trade_value&quot;: &quot;20598695262.2473&quot;,
      &quot;prev_closing_price&quot;: &quot;34484000&quot;,
      &quot;units_traded_24H&quot;: &quot;1488.23398941&quot;,
      &quot;acc_trade_value_24H&quot;: &quot;51466727556.7654&quot;,
      &quot;fluctate_24H&quot;: &quot;1454000&quot;,
      &quot;fluctate_rate_24H&quot;: &quot;4.24&quot;}
    }
 }

### 현재 호가 정보 
 {
  &quot;status&quot;: &quot;0000&quot;,
  &quot;data&quot;: {
    &quot;timestamp&quot;: &quot;1686472242701&quot;,
    &quot;payment_currency&quot;: &quot;KRW&quot;,
    &quot;BTC&quot;: {
      &quot;order_currency&quot;: &quot;BTC&quot;,
      &quot;bids&quot;: [
        {
          &quot;price&quot;: &quot;34131000&quot;,
          &quot;quantity&quot;: &quot;0.1&quot;
        },
        {
          &quot;price&quot;: &quot;34130000&quot;,
          &quot;quantity&quot;: &quot;0.0006&quot;
        },
        {
          &quot;price&quot;: &quot;34128000&quot;,
          &quot;quantity&quot;: &quot;0.0001&quot;
        },
        {
          &quot;price&quot;: &quot;34125000&quot;,
          &quot;quantity&quot;: &quot;0.0075&quot;
        },
        {
          &quot;price&quot;: &quot;34123000&quot;,
          &quot;quantity&quot;: &quot;0.0628&quot;
        }
      ],
      &quot;asks&quot;: [
        {
          &quot;price&quot;: &quot;34135000&quot;,
          &quot;quantity&quot;: &quot;0.2051&quot;
        },
        {
          &quot;price&quot;: &quot;34136000&quot;,
          &quot;quantity&quot;: &quot;0.396&quot;
        },
        {
          &quot;price&quot;: &quot;34137000&quot;,
          &quot;quantity&quot;: &quot;0.0179&quot;
        },
        {
          &quot;price&quot;: &quot;34139000&quot;,
          &quot;quantity&quot;: &quot;0.0795&quot;
        },
        {
          &quot;price&quot;: &quot;34140000&quot;,
          &quot;quantity&quot;: &quot;0.0207&quot;
        }
      ]
    },</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[파이썬 코딩의 기술]]></title>
            <link>https://velog.io/@kingjo_/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%BD%94%EB%94%A9%EC%9D%98-%EA%B8%B0%EC%88%A0</link>
            <guid>https://velog.io/@kingjo_/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%BD%94%EB%94%A9%EC%9D%98-%EA%B8%B0%EC%88%A0</guid>
            <pubDate>Sat, 03 Jun 2023 13:26:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kingjo_/post/e5a4e81b-1272-4677-9203-5ca8eacd9135/image.png" alt=""></p>
<p>전 직장에 신입 개발자로 입사해 몇 달 정도 지났을 때, 파이썬으로 프로젝트를 몇 건 진행하다보니 ENTP의 고질적인 질병인 자아도취가 발생했던 적이 있다. 그렇다. 우매함의 봉우리이다. 나 스스로가 대단한 파이썬 개발자인 양 생각하고 있던 때에, 우연히 CTO께서 직접 본인이 짠 코드를 리뷰해주셨다. 코드를 열어보니... &#39;내가 이제까지 했던 건 파이썬이 아니라 파이썬처럼 생긴 무언가다&#39; 하는 깨달음을 얻고 나서 한동안 울적해졌었다. </p>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/a85c2217-d41a-4a8b-bf79-da4d8b59edcf/image.png" alt=""></p>
<p>물론 이 분야에서 몇십 년을 일해오신 CTO와 내 코드가 같은 수준일 거라고는 기대도 안했지만 나 스스로 내 코딩 실력에 크게 실망한 것은 사실이다. 어찌되었건 개발자로 먹고 살기로 결심했으니, 울적한 상태로 지내서는 이도 저도 안되겠다 싶었다. 마음을 다잡고, 파이썬을 이용한 어떤 주제(ML이나 DL, 데이터 등)보다 먼저 파이썬 자체에 대해서 공부를 해봐야겠다 싶었다. 그 때 산 책이 이 책이다. 전 직장을 퇴사하고, 다른 회사에 들어가서 일하고, 그 사이에 개인적인 여러가지 일을 하는 등 바쁘게 지내다보니(사실 변명이다) 정작 이 책을 완독한 적은 없었다. 최근 여유가 조금 생겼으니, 이 책을 완독해봐야겠다는 생각을 가지고 읽어보았다.</p>
<p>사실 완독하는게 쉬운 일은 아니었다. 으레 개발 서적들이 그러하듯 이 책도 구약성경과 비슷하게 &#39;<del>하지 마라&#39; 혹은 &#39;</del>하라&#39;와 같은 내용이 매우 밀도있게 구성되어 있었고, 한 번에 그 모든 내용을 흡수하는 건 불가능해 보였다. 며칠에 걸쳐 나눠 읽고 난 뒤, 내 나름대로 느낀 점을 정리해보았다. 쓰고 나서 든 생각인데, 느낀 점도 결국 구약성경에 나오는 것처럼 고루하고 원론적인 이야기인 것 같다.</p>
<h3 id="pep8-스타일을-준수할-것">PEP8 스타일을 준수할 것</h3>
<p>내가 진행한 프로젝트들은 대개 1회성에 지나지 않는 것들이라, 개발 시간 엄수가 최고의 가치이며 그 외의 다른 요소(코드 품질, 가독성, 리소스 고려 등)은 중요시하지 않는 환경이었다. 이는 내가 퇴사를 결심하게 된 가장 큰 원인이기도 하다. 더군다나 투입되는 개발자 중 내 코드와 직접적인 연관이 있는 개발자는 많으면 한 명, 보통은 나 혼자인 경우가 대부분이었다. 이런 프로젝트 내에서 PEP(Python Enhancement Proposal)에서 제시하는 코딩 스타일에 대한 준수는 크게 중요시되지 않았다. SI 회사는 언제나 &#39;작동되는지, 작동되지 않는지&#39;만 생각하니까. </p>
<p>당시에도 막연히 든 생각이지만, 수만, 수십만에 달하는 코드가 관여하는 서비스를 운영한다면 이런 식으로 코딩할 수 있을까? 하는 생각이 들었었다. 어쩌다 내 코드를 누군가에게 보여줘야 할 일이 생긴다면, 일일이 함수와 로직을 설명해야 했으니까. 결국 이건 커뮤니케이션 비용이라 할 수 있는데, 팀 내에서 코딩 스타일을 준수하며 개발을 진행하면 이런 커뮤니케이션 비용을 획기적으로 줄일 수 있지 않나 싶었다.
<img src="https://velog.velcdn.com/images/kingjo_/post/1817415c-2a45-4b7e-9371-32b801282494/image.png" alt=""></p>
<p>또한 이런 커뮤니케이션 비용은 나 스스로에게도 해당되는 것 같다. 서비스를 운영한다는 것은, 기존 코드를 유지보수는 과정을 포함하는데, 지저분하게 짜인 코드는 개발자 스스로에게도 해독하는 과정이 필요하다. 이런 상황을 미연에 방지하려면(혹은 유지보수하는 수고를 조금이라도 덜려면) 코딩 스타일을 지키며 개발하는 것이 중요하다 생각했다.</p>
<p>요즘은 Black같은 코딩 스타일 검사 라이브러리도 있으니 이런 부분에 대한 우려를 많이 덜 수 있을 것 같다. 나중에 Black을 통한 코딩 스타일 체크도 포스트 해야겠다.</p>
<h3 id="내장-함수를-적극적으로-사용할-것">내장 함수를 적극적으로 사용할 것</h3>
<p>Python은 정말 많은 내장 함수들이 있고, 버전이 업데이트 될 때마다 개발자의 편의성을 올려주는 많은 기능들이 추가된다. 이런 기능들은 (내가 경험했던 프로젝트에서는) 아는 사람도 별로 없고, 쓰는 사람은 더더욱 없었다. 가끔 보인다 싶으면 구글의 어딘가에서 가져온 코드 속에 포함되어 있던 것이었다. 당연히 어떤 역할을 수행하는지는 관심도 없었다. </p>
<p>나도 지난 1년간 파이썬을 이용한 여러 프로젝트(주로 ML, Data에 관련된)을 진행했었고, 하다보니 여러 파이썬의 초보적인 기능들(리스트 컴프리헨션이나 map, zip, enumerate, lambda같이 잘 알려진 기능들)을 체득하게 되었지만, 이 책을 읽으며 내가 생각했던 것보다 더 많은 내장 함수들이 있으며, 또 내가 알고 있다고 생각했던 그런 내장 함수나 기타 기능들에는 내가 생각해본 적 없는 다양한 활용처가 존재한다는 걸 느꼈다.</p>
<p>이런 함수들을 잘 활용하고, 파이썬 고유의 문법을 잘 살린 코드를 Pythonic하다고 표현하는데, 이런 코드는 리소스 효율 측면에서도 뛰어나겠지만, 일단 가독성에서 큰 차이를 보였던 것 같다. 일단 멋있다! 일전에 누군가 파이썬 개발자를 &#39;코드를 축약하는데 목숨을 거는 사람들&#39;이라고 표현하였는데, 이 관점에서 내장 함수는 필수적인 요소인 것 같다.</p>
<p>&nbsp;</p>
<p>아쉽게도 이 책에서 소개한 용례를 다 이해하지는 못했다. 이후 프로젝트를 진행하면서 지속적으로 다시 읽어보아야 할 것 같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Concurrency Control & Isolation Level]]></title>
            <link>https://velog.io/@kingjo_/Concurrency-Control-Isolation-Level</link>
            <guid>https://velog.io/@kingjo_/Concurrency-Control-Isolation-Level</guid>
            <pubDate>Fri, 02 Jun 2023 02:19:08 GMT</pubDate>
            <description><![CDATA[<p>사내 세미나를 위해 만들었던 장표</p>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/1af420c9-f5a9-4e2d-a13a-24786d77f838/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/eb3a3fbe-6544-40ae-9184-912ab750faf4/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/5b887b22-04de-4255-91a3-6fe4a4b090a4/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/ede63634-ade2-43e3-b58e-412b4fd9e619/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/bda01f17-68ef-4e0b-890a-2804f83bdb88/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/5aa519b0-8d1c-49fe-803a-504ceefc65d8/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/bb3e52f6-40a4-4b00-a276-434b7f1c778c/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/e1b88956-ac45-4175-819e-cbbd697a7589/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/90e1365f-ddff-49ea-9e99-c87f6912d036/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/9eb8856c-0316-4747-9958-f63074452ddb/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/6f066f8b-7626-435a-89e8-9437e4365b75/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/843508df-79f3-4866-8ad6-cd92cf326fd0/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/58786011-7ab9-4b3e-ba74-948bcff26b19/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/31cdb0f9-86ef-4429-b1d2-8ac21365a405/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/65b189ca-3913-4981-82bf-cb769db3eeb4/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Transaction & ACID]]></title>
            <link>https://velog.io/@kingjo_/Transaction-ACID</link>
            <guid>https://velog.io/@kingjo_/Transaction-ACID</guid>
            <pubDate>Fri, 02 Jun 2023 02:10:06 GMT</pubDate>
            <description><![CDATA[<p>사내 세미나 용으로 만들었던 장표</p>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/4e8bdadd-44c5-43a5-94ca-d3b365bef7f9/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/c8fe51a0-c83b-43d9-ae4a-735c88df31fb/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/0bbff499-54b5-44fd-83a4-de9a82386479/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/09e21cb0-fbb7-4108-80dc-f2ebb6e32153/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/300a8d9f-0cfb-40aa-bbe2-daad77116a8c/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/44c43ff0-ce1f-4469-bf58-b942fc25e9b7/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/db2004f0-034b-4ee3-ac20-5b64d8bcc88a/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/kingjo_/post/91895ef3-edb5-4e08-9003-1a3eeb0c59de/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spark Overview]]></title>
            <link>https://velog.io/@kingjo_/Spark-Overview</link>
            <guid>https://velog.io/@kingjo_/Spark-Overview</guid>
            <pubDate>Thu, 01 Jun 2023 08:07:18 GMT</pubDate>
            <description><![CDATA[<p> 지난 면접에서 나 스스로 인정했듯이, 그간 경험했던 프로젝트에서 나는 &#39;<strong>Spark를 사용하여 개발</strong>&#39;한 게 아니라, &#39;<strong>Spark가 있는 환경에서 Python을 사용하여 개발</strong>&#39;을 하고 있었다. </p>
<p> 그만큼 내가 개발을 진행했던 환경(Azure 내의 데이터 서비스들)에서는 Spark를 구성하는 다양한 요소들에 대해 클라우드가 많은 부분을 대신해주고 있었고, 이는 개발 편의성 측면에서 많은 장점이 있겠지만 개발자 본인에게는 약점이 될 수도 있는 부분인 것 같다.</p>
<p> 따라서 단순히 &#39;Spark를 본 적이 있다, Spark에서 데이터 처리하는 코드 짤 수 있다&#39; 수준을 넘어서기 위해서는 Spark의 내부 구조를 파악하고, 이를 사용하는 사이드 프로젝트를 진행해보는 것이 현재 단계에서는 필요한 경험이라 생각되었다. 이 문서를 작성하는 이유는, 이 경험의 첫 단계로 Spark를 제대로 공부해보기 위함이다. 분명 언젠가는 도움이 되겠지...</p>
<h3 id="spark-개요">Spark 개요</h3>
<p>Apache Spark는 널리 알려진 대로 데이터 분산처리 프레임워크로, 이전의 Hadoop MapReduce의 성능상 한계를 극복하기 위해 만들어진 프레임워크이다. </p>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/fed41304-e9ba-4eae-afc7-37297722f680/image.png" alt=""></p>
<p>기존의 Hadoop 기반의 분산처리 시스템과는 다르게, Spark 상에서는 Batch Processing 뿐만 아니라 Streaming도 가능하다. 또한 Scala, R, Python 등 여러 언어로 접근 가능하다는 장점이 있으며, MLlib, GraphX등 다양한 라이브러리의 지원을 받을 수 있다.</p>
<p>또한 Spark의 장점 중 하나는 데이터 처리가 Memory 내에서 이루어지므로 Disk 내에서 처리하는 Hadoop에 비해 월등히 뛰어난 성능을 보장한다는 점이다. 이러한 장점으로 인하여 Hadoop이 차지하고 있던 데이터 분산처리의 기술적 표준을 빠르게 차지하였다.</p>
<p>다만 Spark가 Hadoop을 대체하는 상위호환 개념은 아니다. Spark는 Hadoop Ecosystem 내의 다양한 컴포너트(HBase, Hive 등)과 연동할 수 있으며, 후술할 Cluster Manager로 Hadoop YARN을 사용할 수 있는등 Hadoop과 상호보완적인 프레임워크라고 할 수 있다.</p>
<h3 id="spark-아키텍처">Spark 아키텍처</h3>
<p> <img src="https://velog.velcdn.com/images/kingjo_/post/8534f6ae-89ee-4c6a-a2b2-4c73056bf83c/image.png" alt=""></p>
<p>Spark는 Application(Driver, Worker)과 Cluster Manager로 구성된다.</p>
<p>Driver Program :  Spark의 메인 프로그램으로, 클러스터 내의 마스터 노드에서 실행된다. 리소스 관리나 Worker node에 대한 작업 스케줄링을 담당한다. SparkContext는 일종의 진입점으로, 클러스터 매니저와의 통신하며 Task를 배포하고, 자원을 관리한다.</p>
<p>Worker Node : Driver로부터 할당받은 Task를 처리하는 노드. 내부에는 Executor라는 프로세스가 있는데, Worker Node는 이 프로세스를 실행하여 Task를 처리한다. Worker Node는 일반적으로 여러 개가 존재하며, 하나만 있을 시 Single Node라고 표현한다.</p>
<p>Cluster Manager : 리소스 관리, 고가용성 보장, 클러스터의 확장 및 축소를 담당한다. 여러 종류의 클러스터 매니저가 있는데, 스파크 내장 Stand-Alone, Apache Mesos, Hadoop YARN, Kubernetes 등이 있다.</p>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/85ef45b9-12ec-4816-a31f-9809ee5f13dc/image.png" alt=""></p>
<p>이 그림이 좀 더 직관적인 모습인 것 같다. 그림에서 확인할 수 있듯 SparkSession(2.0 이후, 그 전에는 SparkContext)은 코드와 통신하여 작업을 처리한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[면접에서 풀었던 라이브코딩]]></title>
            <link>https://velog.io/@kingjo_/%EB%A9%B4%EC%A0%91%EC%97%90%EC%84%9C-%ED%92%80%EC%97%88%EB%8D%98-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EC%BD%94%EB%94%A9</link>
            <guid>https://velog.io/@kingjo_/%EB%A9%B4%EC%A0%91%EC%97%90%EC%84%9C-%ED%92%80%EC%97%88%EB%8D%98-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EC%BD%94%EB%94%A9</guid>
            <pubDate>Tue, 30 May 2023 01:35:33 GMT</pubDate>
            <description><![CDATA[<p>문제 설명</p>
<p>2016년 1월 1일은 금요일입니다. 2016년 a월 b일은 무슨 요일일까요? 두 수 a ,b를 입력받아 2016년 a월 b일이 무슨 요일인지 리턴하는 함수, solution을 완성하세요. 요일의 이름은 일요일부터 토요일까지</p>
<p>각각 SUN,MON,TUE,WED,THU,FRI,SAT 입니다.</p>
<p>예를 들어 a=5, b=24라면 5월 24일은 화요일이므로 문자열 &quot;TUE&quot;를 반환하세요.</p>
<p>&nbsp; </p>
<p>문제 풀이</p>
<pre><code> def date_calculator(month, day) : 
    date = [&#39;FRI&#39;, &#39;SAT&#39;, &#39;SUN&#39;, &#39;MON&#39;, &#39;TUE&#39;, &#39;WED&#39;, &#39;THU&#39;]
    month_list = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 31, 31]
    result = date[(sum(month_list[:-month])+day-1)%7]

    return result</code></pre><p>리스트 연산만 잘 하면 되는 문제였다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Pandas 여러 기능들]]></title>
            <link>https://velog.io/@kingjo_/Pandas-%EC%97%AC%EB%9F%AC-%EA%B8%B0%EB%8A%A5%EB%93%A4</link>
            <guid>https://velog.io/@kingjo_/Pandas-%EC%97%AC%EB%9F%AC-%EA%B8%B0%EB%8A%A5%EB%93%A4</guid>
            <pubDate>Fri, 19 May 2023 01:16:05 GMT</pubDate>
            <description><![CDATA[<pre><code>import pandas as pd

# df 생성
data = {
    &#39;이름&#39;: [&#39;홍길동&#39;, &#39;김철수&#39;, &#39;이영희&#39;, &#39;박영진&#39;, &#39;최지원&#39;],
    &#39;성별&#39;: [&#39;남&#39;, &#39;남&#39;, &#39;여&#39;, &#39;남&#39;, &#39;여&#39;],
    &#39;나이&#39;: [18, 19, 17, 20, 18],
    &#39;국어&#39;: [85, 92, 78, 90, 88],
    &#39;영어&#39;: [78, 88, 92, 85, 90],
    &#39;수학&#39;: [90, 85, 88, 92, 78]
}

df = pd.DataFrame(data)

# 중복 데이터 제거
df_duplicates_removed = df.drop_duplicates()

# 결측치 확인
has_missing_values = df.isnull().values.any()

# 특정 컬럼 선택
selected_column = df[&#39;이름&#39;]

# 조건에 따른 컬럼 선택
filtered_columns = df.loc[df[&#39;나이&#39;] &gt; 18, [&#39;이름&#39;, &#39;국어&#39;, &#39;영어&#39;]]

# 특정 컬럼 제거
df_dropped = df.drop(&#39;성별&#39;, axis=1)

# 컬럼 순서 변경
df_reordered = df[[&#39;나이&#39;, &#39;성별&#39;, &#39;이름&#39;, &#39;국어&#39;, &#39;영어&#39;, &#39;수학&#39;]]

# 행 인덱스 재설정
df_reset_index = df.reset_index(drop=True)

# 특정 조건에 따른 행 필터링
filtered_rows = df[df[&#39;국어&#39;] &gt; 90]

# 행과 열 조건에 따른 데이터 선택
filtered_data = df.loc[(df[&#39;국어&#39;] &gt; 90) &amp; (df[&#39;수학&#39;] &gt; 90), [&#39;이름&#39;, &#39;국어&#39;, &#39;수학&#39;]]

# 특정 컬럼의 고유값 확인
unique_values = df[&#39;성별&#39;].unique()

# 컬럼 값에 따른 행 개수 세기
value_counts = df[&#39;성별&#39;].value_counts()

# 데이터프레임 병합
df2 = pd.DataFrame({&#39;이름&#39;: [&#39;홍길동&#39;, &#39;김철수&#39;, &#39;이영희&#39;],
                    &#39;예체능&#39;: [&#39;미술&#39;, &#39;음악&#39;, &#39;체육&#39;]})
merged_df = pd.merge(df, df2, on=&#39;이름&#39;, how=&#39;left&#39;)

# 특정 컬럼의 문자열 연산
df[&#39;이름_대문자&#39;] = df[&#39;이름&#39;].str.upper()

# 날짜 데이터 다루기
df[&#39;날짜&#39;] = pd.to_datetime(&#39;2022-01-01&#39;)
df[&#39;연도&#39;] = df[&#39;날짜&#39;].dt.year
df[&#39;월&#39;] = df[&#39;날짜&#39;].dt.month

# 컬럼의 데이터 타입 변경
df[&#39;국어&#39;] = df[&#39;국어&#39;].astype(float)

# 데이터 그룹화 후 연산
grouped_data = df.groupby(&#39;성별&#39;).agg({&#39;국어&#39;: &#39;mean&#39;, &#39;수학&#39;: &#39;max&#39;})</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[ML Engineering & DevOps]]></title>
            <link>https://velog.io/@kingjo_/MLEngineering</link>
            <guid>https://velog.io/@kingjo_/MLEngineering</guid>
            <pubDate>Thu, 18 May 2023 01:22:20 GMT</pubDate>
            <description><![CDATA[<h5 id="블로그-이전-글">블로그 이전 글</h5>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/13c1f6d0-9422-4c21-91a0-d26cd6ea108d/image.png" alt=""></p>
<h3 id="machine-learning-engineering">Machine Learning Engineering</h3>
<p>머신러닝은, 정말 극단적으로 축약해서 표현하자면 &quot;학습하고, 예측하는&quot; 것입니다. 하지만 실제 머신러닝을 어딘가에 적용하기 위해서는, 다음과 같은 사항을 고려해야 합니다.</p>
<p>a. 데이터는 어디서, 어떻게 오는가?</p>
<p>b. 예측은 잘 수행되고 있는가?</p>
<p>c. 예측 Output은 어디로, 어떻게 가는가?</p>
<p>간단하게 표현하자면 a는 데이터, b는 모델, c는 서비스(혹은 코드)에 대한 문제입니다. kaggle에 제출하거나, 아니면 단순 취미로써 머신러닝을 돌리는 게 아니라면 위 문제들에 대해서는 반드시 생각하여야 합니다. 실제로 대부분의 서비스에서 머신러닝 코드가 차지하는 비중은 5~10%도 되지 않으며, a, b, c에 대한 코드가 대부분을 차지합니다. 하나씩 살펴보겠습니다.</p>
<p>a. 데이터는 어디서, 어떻게 오는가?</p>
<p>서비스 환경에서 머신러닝 모델은 한번 학습하고 한번 예측하는 것이 아닌, 배치 프로세스가 돌 때마다 학습과 예측 활동을 반복해야 합니다. 그러려면 데이터가 지속적으로 공급이 되어야하고, 데이터가 공급되기 위해서는 일종의 파이프라인이 구축되야 한다는 것을 의미합니다. 이 파이프라인은 데이터에 관한 일련의 ETL 과정을 진행하여 모델로 데이터를 (학습과 예측 양면으로) 공급합니다,</p>
<p>b. 예측은 잘 수행되고 있는가?</p>
<p> 아무리 정교한 모델이라 하더라도 시간이 지나면 데이터셋이 변화됨에 따라 모델의 성능이 떨어집니다. 따라서 예측을 잘 수행하고자 한다면 주기적으로 데이터셋의 특성을 다시 파악하고, 모델을 재학습시키고, 성능을 평가하는 과정을 거쳐야 합니다. 상황에 따라서는 과거 모델을 쓸 수도 있으니 각 모델에 대한 버전 관리를 고려해야 할 수도 있습니다.</p>
<p>c. 예측 Output은 어디로, 어떻게 가는가?</p>
<p> 예측 결과는 떼어놓고만 보면 어떤 Integer, 아니면 Float, 아니며 String의 집합 자료형일 뿐입니다. 이 예측 결과를 가지고 어떤 서비스를 제공하고자 한다면 이 예측 결과를 어떤 로직 안에서 활용해야 합니다. 즉 예측 결과를 가공하여 전송하는 파이프라인 또한 구축이 되어야 합니다.</p>
<p> 이상의 과정을 <strong>머신러닝 엔지니어링(Machine Learning Engineering)</strong> 이라고 표현합니다. 다르게 말하자면 머신러닝 엔지니어링은 단순히 머신러닝을 돌리는 것이 아닌, 머신러닝은 물론이고 데이터, 서비스 측면을 포함한 일련의 시스템을 구축하는 분야라고 할 수 있겠습니다. 머신러닝 프로젝트를 진행할 때에는 반드시 위 3가지 측면에 대해서 생각하고 있어야 합니다!</p>
<h3 id="mlops란">MLOps란?</h3>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/24e3219c-3607-4cf4-85e7-8be42cfd6ecd/image.png" alt=""></p>
<p> 개발자라면 아마 DevOps라는 개념, 혹은 직군에 대한 이야기를 들어보신 적이 있을 겁니다. DevOps란, 일반적인 정의에 의하면, &quot;개발과 운영의 통합&quot;이라고 볼 수 있을 것 같습니다.
 여기서 개발이란 &quot;시스템을 만드는 사람들&quot;, 즉 우리같은 사람들을 뜻하며, 운영이랑 &quot;시스템을 유지하는 사람들&quot;을 뜻합니다. (게임 개발자와 GM을 생각하시면 직관적으로 이해할 수 있습니다.) 이 두 부서는 오랜 기간 동안 분리되어 있었으며, 따라서 운영 중 생기는 장애, 혹은 개선사항에 대해서는 많은 문서와, 많은 시간이 투입되어야 실제 서비스에 반영이 되었습니다. 그러나 JIRA, Jenkins 등의 Devops 툴의 등장으로 개발과 운영을 한 사이클 내에서 관리할 수 있게 되었습니다. </p>
<p> DevOps의 장점은 매우 명확합니다. 개발과 운영 사이에 간극이 좁아짐에 따라 서비스의 오류 및 수정 사항이 빠르게 반영되며, 이상의 과정이 (DevOps를 설명하는 그림에서 흔히 볼 수 있듯) 하나의 순환주기를 이루게 됨으로써, 서비스는 지속적으로 개선될 수 있습니다. 듣기에 따라서는 당연한 이야기일지 모르겠으나, DevOps는 기존의 Waterfall 개발만 해왔던 조직에 비해 굉장한 이점을 제공합니다!</p>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/971ccd66-5bba-4c36-8a38-43103412c60e/image.png" alt=""></p>
<p>머신러닝 서비스를 제공하는 기업에게도 DevOps를 적용할 수 있습니다. 다만 일반적인 DevOps환경보다는 신경써야 할 것이 더 많습니다. </p>
<p><strong>Testing</strong>
일반적인 단위, 통합 테스트 외에 데이터 검증, 학습된 모델 품질 평가, 모델 검증 등의 과정</p>
<p><strong>Deployment</strong>
오프라인에서 학습된 ML모델을 배포하는 수준에 그치는 것이 아니라, 새 모델을 재학습하고, 검증하는 과정</p>
<p><strong>Production</strong>
일반적으로 알고리즘과 로직의 최적화를 통해 최적의 성능을 낼 수 있는 소프트웨어 시스템과 달리, ML 모델은 이에 더해서 지속적으로 진화하는 data profile 자체만으로도 성능이 저하될 수 있습니다.
즉, 기존 소프트웨어 시스템보다 더 다양한 이유로 성능이 손상될 수 있으므로, 데이터의 Summary Statistics를 꾸준히 추적하고, 모델의 온라인 성능을 모니터링하여 값이 기대치를 벗어나면 알림을 전송하거나 롤백을 할 수 있어야 합니다.</p>
<p><strong>CI (Continuous Integration)</strong>
Code와 Components뿐만 아니라 Data, Data Schema, Model에 대해 모두 테스트되고 검증.</p>
<p><strong>CD (Continuous Delivery)</strong>
단일 소프트웨어 패키지가 아니라 ML 학습 파이프라인 전체를 배포.</p>
<p><strong>CT (Continuous Training)</strong>
ML 시스템만의 속성으로, 모델을 자동으로 학습시키고 평가하는 단계.</p>
<p><img src="https://velog.velcdn.com/images/kingjo_/post/e532267b-7c2e-419c-9475-4dfce5c467cc/image.png" alt=""></p>
<p>이상의 요소들을 자동화시킴으로써 잘 구축된 MLOps 환경은 엔지니어가 로그 대시보드, 슬랙 메신저만 확인해도 될만큼 뛰어난 안정성과 편의성을 보입니다.</p>
]]></description>
        </item>
    </channel>
</rss>