<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://kinkos1234.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kinkos1234.github.io/" rel="alternate" type="text/html" /><updated>2026-04-25T13:59:10+09:00</updated><id>https://kinkos1234.github.io/feed.xml</id><title type="html">Comad.J</title><subtitle>우주를 유영합니다</subtitle><author><name>Comad.J</name></author><entry><title type="html">[Python] 30 Days of Streamlit 완주기: 데이터 앱 프로토타이핑의 신세계</title><link href="https://kinkos1234.github.io/coding/streamlit30days/" rel="alternate" type="text/html" title="[Python] 30 Days of Streamlit 완주기: 데이터 앱 프로토타이핑의 신세계" /><published>2026-02-09T00:00:00+09:00</published><updated>2026-02-09T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/streamlit30days</id><content type="html" xml:base="https://kinkos1234.github.io/coding/streamlit30days/"><![CDATA[<h1 id="30-days-of-streamlit-빠르고-강력한-데이터-웹앱-만들기">30 Days of Streamlit: 빠르고 강력한 데이터 웹앱 만들기</h1>

<p>최근 파이썬 데이터 생태계를 공부하면서 Pandas나 Plotly 같은 라이브러리를 통해 데이터를 분석하고 시각화하는 방법을 다뤄왔다. 하지만 분석 결과를 나만 보는 것이 아니라 다른 사람과 웹 상에서 공유하고 인터랙션하게 만들려면 프론트엔드 작업이 필수적이다.</p>

<p>이 간극을 채워주는 가장 완벽한 도구가 바로 <strong>Streamlit(스트림릿)</strong>이다. 단순히 파이썬 스크립트 몇 줄만 작성하면 번거로운 HTML, CSS, JS 작성 없이도 곧바로 웹 애플리케이션이 뚝딱 만들어진다. 이번에는 공식 홈페이지에서 제공하는 <a href="https://30days.streamlit.app/">30 Days of Streamlit</a> 챌린지를 따라가보며 핵심 기능들을 실습해봤다.</p>

<hr />

<h2 id="1-챌린지를-통한-핵심-기능-완스터디">1. 챌린지를 통한 핵심 기능 완스터디</h2>

<p>30일 챌린지는 단순 텍스트(<code class="language-plaintext highlighter-rouge">st.write</code>) 출력부터 시작해서, 체크박스나 슬라이더 같은 기본 위젯, 그리고 캐싱(<code class="language-plaintext highlighter-rouge">@st.cache_data</code>)과 세션 상태(<code class="language-plaintext highlighter-rouge">st.session_state</code>)를 활용한 최적화 알고리즘까지 점진적으로 난이도가 올라간다.</p>

<p>가장 인상적이었던 두 가지 실습을 꼽아 정리해본다.</p>

<h3 id="11-간결함의-미학-유튜브-썸네일-추출기">1.1 간결함의 미학: 유튜브 썸네일 추출기</h3>

<p>웹 개발 경험이 있다면, 사용자에게 URL을 입력받고 외부 API로 이미지를 긁어와 화면에 뿌려주는 작업이 은근히 손이 많이 간다는 걸 알 것이다. 스트림릿에서는 이 과정이 믿을 수 없을 만큼 간결하다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">streamlit</span> <span class="k">as</span> <span class="n">st</span>

<span class="n">st</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">'🖼️ yt-img-app'</span><span class="p">)</span>
<span class="n">st</span><span class="p">.</span><span class="n">header</span><span class="p">(</span><span class="s">'YouTube Thumbnail Image Extractor App'</span><span class="p">)</span>

<span class="c1"># 사이드바에서 이미지 화질 셋팅 메뉴 구성
</span><span class="n">st</span><span class="p">.</span><span class="n">sidebar</span><span class="p">.</span><span class="n">header</span><span class="p">(</span><span class="s">'Settings'</span><span class="p">)</span>
<span class="n">img_dict</span> <span class="o">=</span> <span class="p">{</span><span class="s">'Max'</span><span class="p">:</span> <span class="s">'maxresdefault'</span><span class="p">,</span> <span class="s">'High'</span><span class="p">:</span> <span class="s">'hqdefault'</span><span class="p">,</span> <span class="s">'Medium'</span><span class="p">:</span> <span class="s">'mqdefault'</span><span class="p">,</span> <span class="s">'Standard'</span><span class="p">:</span> <span class="s">'sddefault'</span><span class="p">}</span>
<span class="n">selected_img_quality</span> <span class="o">=</span> <span class="n">st</span><span class="p">.</span><span class="n">sidebar</span><span class="p">.</span><span class="n">selectbox</span><span class="p">(</span><span class="s">'Select image quality'</span><span class="p">,</span> <span class="p">[</span><span class="s">'Max'</span><span class="p">,</span> <span class="s">'High'</span><span class="p">,</span> <span class="s">'Medium'</span><span class="p">,</span> <span class="s">'Standard'</span><span class="p">])</span>
<span class="n">img_quality</span> <span class="o">=</span> <span class="n">img_dict</span><span class="p">[</span><span class="n">selected_img_quality</span><span class="p">]</span>

<span class="n">yt_url</span> <span class="o">=</span> <span class="n">st</span><span class="p">.</span><span class="n">text_input</span><span class="p">(</span><span class="s">'Paste YouTube URL'</span><span class="p">,</span> <span class="s">'https://youtu.be/JwSS70SZdyM'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_ytid</span><span class="p">(</span><span class="n">input_url</span><span class="p">):</span>
  <span class="k">if</span> <span class="s">'youtu.be'</span> <span class="ow">in</span> <span class="n">input_url</span><span class="p">:</span>
    <span class="n">ytid</span> <span class="o">=</span> <span class="n">input_url</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'/'</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
  <span class="k">if</span> <span class="s">'youtube.com'</span> <span class="ow">in</span> <span class="n">input_url</span><span class="p">:</span>
    <span class="n">ytid</span> <span class="o">=</span> <span class="n">input_url</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'='</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
  <span class="k">return</span> <span class="n">ytid</span>

<span class="k">if</span> <span class="n">yt_url</span> <span class="o">!=</span> <span class="s">''</span><span class="p">:</span>
  <span class="n">ytid</span> <span class="o">=</span> <span class="n">get_ytid</span><span class="p">(</span><span class="n">yt_url</span><span class="p">)</span>
  <span class="n">yt_img</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'http://img.youtube.com/vi/</span><span class="si">{</span><span class="n">ytid</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">img_quality</span><span class="si">}</span><span class="s">.jpg'</span>
  <span class="n">st</span><span class="p">.</span><span class="n">image</span><span class="p">(</span><span class="n">yt_img</span><span class="p">)</span>
  <span class="n">st</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">'YouTube video thumbnail image URL: '</span><span class="p">,</span> <span class="n">yt_img</span><span class="p">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">st.sidebar.selectbox</code>로 좌측 셋팅바를 순식간에 구현하고, <code class="language-plaintext highlighter-rouge">st.text_input</code>으로 URL을 받자마자 파이썬의 문자열 파싱 로직을 거쳐 <code class="language-plaintext highlighter-rouge">st.image</code>로 뿌려주는 구조다. 복잡한 라우팅이나 상태 관리 없이 코드가 위에서 아래로 물 흐르듯 실행된다는 점이 매력적이다.</p>

<h3 id="12-프레임워크의-확장-streamlit-elements-대시보드">1.2 프레임워크의 확장: Streamlit Elements 대시보드</h3>

<p>Day 27에서는 <code class="language-plaintext highlighter-rouge">streamlit-elements</code>를 사용해 크기 조절과 드래그 앤 드롭이 가능한 대화형 대시보드를 구성하는 실습을 진행했다. 기본 제공 위젯만으로는 아쉬운 고차원적 UI 구성을 Material UI, Nivo 차트, Monaco 에디터 등을 융합해 해결하는 방법이다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">streamlit_elements</span> <span class="kn">import</span> <span class="n">elements</span><span class="p">,</span> <span class="n">dashboard</span><span class="p">,</span> <span class="n">mui</span><span class="p">,</span> <span class="n">editor</span><span class="p">,</span> <span class="n">media</span><span class="p">,</span> <span class="n">lazy</span><span class="p">,</span> <span class="n">sync</span><span class="p">,</span> <span class="n">nivo</span>

<span class="c1"># ... 중략 (데이터 초기화 로직) ...
</span>
<span class="n">layout</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">dashboard</span><span class="p">.</span><span class="n">Item</span><span class="p">(</span><span class="s">"editor"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span>
    <span class="n">dashboard</span><span class="p">.</span><span class="n">Item</span><span class="p">(</span><span class="s">"chart"</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span>
    <span class="n">dashboard</span><span class="p">.</span><span class="n">Item</span><span class="p">(</span><span class="s">"media"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
<span class="p">]</span>

<span class="k">with</span> <span class="n">elements</span><span class="p">(</span><span class="s">"demo"</span><span class="p">):</span>
    <span class="k">with</span> <span class="n">dashboard</span><span class="p">.</span><span class="n">Grid</span><span class="p">(</span><span class="n">layout</span><span class="p">,</span> <span class="n">draggableHandle</span><span class="o">=</span><span class="s">".draggable"</span><span class="p">):</span>

        <span class="c1"># 1. 코드 에디터 (Monaco)
</span>        <span class="k">with</span> <span class="n">mui</span><span class="p">.</span><span class="n">Card</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="s">"editor"</span><span class="p">,</span> <span class="n">sx</span><span class="o">=</span><span class="p">{</span><span class="s">"display"</span><span class="p">:</span> <span class="s">"flex"</span><span class="p">,</span> <span class="s">"flexDirection"</span><span class="p">:</span> <span class="s">"column"</span><span class="p">}):</span>
            <span class="n">mui</span><span class="p">.</span><span class="n">CardHeader</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s">"Editor"</span><span class="p">,</span> <span class="n">className</span><span class="o">=</span><span class="s">"draggable"</span><span class="p">)</span>
            <span class="k">with</span> <span class="n">mui</span><span class="p">.</span><span class="n">CardContent</span><span class="p">(</span><span class="n">sx</span><span class="o">=</span><span class="p">{</span><span class="s">"flex"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"minHeight"</span><span class="p">:</span> <span class="mi">0</span><span class="p">}):</span>
                <span class="n">editor</span><span class="p">.</span><span class="n">Monaco</span><span class="p">(</span>
                    <span class="n">defaultValue</span><span class="o">=</span><span class="n">st</span><span class="p">.</span><span class="n">session_state</span><span class="p">.</span><span class="n">data</span><span class="p">,</span>
                    <span class="n">language</span><span class="o">=</span><span class="s">"json"</span><span class="p">,</span>
                    <span class="n">onChange</span><span class="o">=</span><span class="n">lazy</span><span class="p">(</span><span class="n">sync</span><span class="p">(</span><span class="s">"data"</span><span class="p">))</span>
                <span class="p">)</span>
            <span class="k">with</span> <span class="n">mui</span><span class="p">.</span><span class="n">CardActions</span><span class="p">:</span>
                <span class="n">mui</span><span class="p">.</span><span class="n">Button</span><span class="p">(</span><span class="s">"Apply changes"</span><span class="p">,</span> <span class="n">onClick</span><span class="o">=</span><span class="n">sync</span><span class="p">())</span>

        <span class="c1"># 2. 범프 차트 (Nivo Bump Chart)
</span>        <span class="k">with</span> <span class="n">mui</span><span class="p">.</span><span class="n">Card</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="s">"chart"</span><span class="p">,</span> <span class="n">sx</span><span class="o">=</span><span class="p">{</span><span class="s">"display"</span><span class="p">:</span> <span class="s">"flex"</span><span class="p">,</span> <span class="s">"flexDirection"</span><span class="p">:</span> <span class="s">"column"</span><span class="p">}):</span>
            <span class="n">mui</span><span class="p">.</span><span class="n">CardHeader</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s">"Chart"</span><span class="p">,</span> <span class="n">className</span><span class="o">=</span><span class="s">"draggable"</span><span class="p">)</span>
            <span class="k">with</span> <span class="n">mui</span><span class="p">.</span><span class="n">CardContent</span><span class="p">(</span><span class="n">sx</span><span class="o">=</span><span class="p">{</span><span class="s">"flex"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"minHeight"</span><span class="p">:</span> <span class="mi">0</span><span class="p">}):</span>
                <span class="n">nivo</span><span class="p">.</span><span class="n">Bump</span><span class="p">(</span>
                    <span class="n">data</span><span class="o">=</span><span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">session_state</span><span class="p">.</span><span class="n">data</span><span class="p">),</span>
                    <span class="c1"># 차트 파라미터 세팅...
</span>                <span class="p">)</span>
</code></pre></div></div>

<p>처음에는 React 기반의 컴포넌트를 파이썬 코드 안으로 가져오는 개념을 이해하는 게 조금 어려웠다. 하지만 원리는 명확했다. 에디터 컴포넌트(<code class="language-plaintext highlighter-rouge">Monaco</code>)의 <code class="language-plaintext highlighter-rouge">onChange</code> 이벤트에 <code class="language-plaintext highlighter-rouge">sync("data")</code>를 묶어 Streamlit의 세션 상태(<code class="language-plaintext highlighter-rouge">st.session_state.data</code>)와 실시간으로 동기화시키고, 이를 옆에 있는 차트(<code class="language-plaintext highlighter-rouge">Nivo Bump</code>)가 바라보게 만드는 것이다.</p>

<p><code class="language-plaintext highlighter-rouge">lazy()</code> 래퍼를 통해 의미 없는 리렌더링을 방지하는 최적화 패턴까지, 인터랙티브 대시보드의 기초 체력을 다지는데 큰 도움이 되었다.</p>

<hr />

<h2 id="2-실습-중-마주한-고민과-해결-방식">2. 실습 중 마주한 고민과 해결 방식</h2>

<p>이번 30일 챌린지를 진행하며 느낀 가장 큰 허들은 바로 <strong>“Streamlit의 작동 방식(실행 흐름)에 대한 이해”</strong>였다.</p>

<p>일반적인 웹이나 GUI 앱은 이벤트 기반으로 돌지만, 시스템의 입력값이 변할 때마다 Streamlit은 냅다 <strong>전체 스크립트를 재실행(Rerun)</strong>해버린다. 편리하지만 무거운 연산이 끼어있다면 극악의 성능 저하를 부를 수 있다.</p>

<p>이를 방지하기 위해 사용했던 핵심 무기들이 있다.</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">@st.cache_data</code> 머신러닝 모델 로드나 무거운 CSV 파일 읽기에 데코레이터를 붙여 한 번 얹어둔 데이터를 재사용했다.</li>
  <li><code class="language-plaintext highlighter-rouge">st.session_state</code> 버튼을 누르거나 페이지가 새로고침되어도 유지되어야 하는 상태값을 관리할 수 있었다. 폼(Form)을 활용한 일괄 제출(<code class="language-plaintext highlighter-rouge">st.form_submit_button</code>)도 렌더링을 최소화하는데 유용했다.</li>
</ol>

<hr />

<h2 id="3-향후-실제-프로젝트-적용-방향">3. 향후 실제 프로젝트 적용 방향</h2>

<p>이번 챌린지를 기점으로 파이썬 코드를 웹 기반 인터페이스로 단번에 띄우는 역량을 갖추게 되었다. 앞으로의 실제 장기 프로젝트나 업무 환경에서는 다음과 같이 적극 도입할 예정이다.</p>

<ul>
  <li><strong>AI 및 LLM 챗봇 프로토타이핑:</strong> 복잡한 백엔드 서버 없이도 사용자와 상호작용 가능한 프롬프트 테스트 앱을 기획하고 빠르게 내부 리뷰를 받을 목적으로 활용.</li>
  <li><strong>데이터 분석 대시보드 사내 배포:</strong> 앞서 학습한 Plotly와 Pandas의 결과물을 Streamlit 앱에 결합해 의사결정권자가 직접 필터나 슬라이더를 돌려가며 데이터를 탐색할 수 있는 BI 도구를 구축.</li>
  <li><strong>머신러닝 파라미터 튜너 제작:</strong> <code class="language-plaintext highlighter-rouge">st.slider</code>나 <code class="language-plaintext highlighter-rouge">st.number_input</code>으로 하이퍼파라미터를 입력받고 실시간으로 SHAP value나 모델 정확도를 렌더링하는 디버깅 포탈 제작.</li>
</ul>

<hr />

<h2 id="마무리">마무리</h2>

<p>React나 Vue처럼 본격적인 클라이언트 사이드 고도화를 노린다면 한계가 있을 수 있겠지만, “데이터 툴”이나 “AI 모델 시연”이라는 도메인 한정으로는 Streamlit만큼 속도감 있고 훌륭한 생산성을 보여주는 프레임워크는 드물다. 기본기를 단단히 다진 만큼, 이제는 배운 것들을 응용하여 작은 나만의 도구들을 하나씩 찍어낼 일만 남았다.</p>

<ul>
  <li>작업 코드 보기: https://github.com/kinkos1234/python_study/tree/main/Streamlit</li>
</ul>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Python" /><category term="Streamlit" /><category term="WebApp" /><category term="Dashboard" /><category term="Data" /><summary type="html"><![CDATA[30 Days of Streamlit: 빠르고 강력한 데이터 웹앱 만들기]]></summary></entry><entry><title type="html">Claude Opus 4.6 vs GPT-5.3 Codex - 코딩 도구에서 ‘일하는 동료’로의 진화</title><link href="https://kinkos1234.github.io/gen_ai/Opus_vs_Codex/" rel="alternate" type="text/html" title="Claude Opus 4.6 vs GPT-5.3 Codex - 코딩 도구에서 ‘일하는 동료’로의 진화" /><published>2026-02-06T00:00:00+09:00</published><updated>2026-02-06T00:00:00+09:00</updated><id>https://kinkos1234.github.io/gen_ai/Opus_vs_Codex</id><content type="html" xml:base="https://kinkos1234.github.io/gen_ai/Opus_vs_Codex/"><![CDATA[<h1 id="claude-opus-46-vs-gpt-53-codex-심층-비교">Claude Opus 4.6 vs GPT-5.3 Codex 심층 비교</h1>

<h2 id="들어가며-같은-날-같은-전쟁터">들어가며: 같은 날, 같은 전쟁터</h2>

<p>2026년 2월 5일, AI 업계의 양대 산맥인 Anthropic과 OpenAI가 약속이나 한 듯 동시에 차세대 모델을 내놓았다. <strong>Claude Opus 4.6</strong>과 <strong>GPT-5.3-Codex</strong>가 그 주인공이다.</p>

<p>두 모델 모두 단순한 성능 개선을 넘어, “AI가 코딩을 도와주는 도구”에서 “스스로 생각하고 일하는 동료”로의 정체성 전환을 선언했다는 점에서 의미가 크다. 이번 포스팅에서는 각 모델의 핵심 변화를 분석하고, 이 동시 출시가 AI 생태계에 던지는 메시지를 정리해본다.</p>

<hr />

<h2 id="목차">목차</h2>

<ol>
  <li>Claude Opus 4.6: 팀으로 일하는 AI</li>
  <li>GPT-5.3-Codex: 스스로 자신을 만든 AI</li>
  <li>정면 승부: 스펙 및 성능 비교</li>
  <li>이번 출시가 말해주는 것</li>
  <li>우려와 한계</li>
  <li>마무리</li>
</ol>

<hr />

<h2 id="1-claude-opus-46-팀으로-일하는-ai">1. Claude Opus 4.6: 팀으로 일하는 AI</h2>

<p>Anthropic은 Opus 4.5 출시 불과 2개월 만에 4.6 버전을 선보였다. 버전 넘버링은 소폭 상승이지만, 담고 있는 철학의 변화는 작지 않다.</p>

<h3 id="11-에이전트-팀-agent-teams">1.1 에이전트 팀 (Agent Teams)</h3>

<p>이번 업데이트의 백미다. 기존 AI가 “만능 프리랜서”처럼 혼자 모든 일을 처리했다면, Opus 4.6은 <strong>“역할 분담이 된 팀”</strong>처럼 작동한다. 여러 에이전트가 각자의 전문 영역(기획, 코딩, 리뷰 등)을 맡아 병렬로 작업을 수행하고 조율한다. 이는 복잡한 대규모 프로젝트를 처리할 때 효율성과 정확도를 비약적으로 높여준다.</p>

<h3 id="12-컨텍스트-윈도우-100만-토큰">1.2 컨텍스트 윈도우 100만 토큰</h3>

<p>Opus 계열 최초로 <strong>100만 토큰</strong>을 지원한다(베타). Opus 4.5(20만 토큰) 대비 5배 늘어난 수치다. 책 수십 권, 혹은 거대한 레거시 코드베이스를 통째로 메모리에 올리고 작업할 수 있게 되었다.</p>

<h3 id="13-적응형-사고-adaptive-thinking">1.3 적응형 사고 (Adaptive Thinking)</h3>

<p>“생각하는 시간”을 AI가 상황에 맞춰 스스로 조절한다. 간단한 질문에는 즉답을, 심층 분석이 필요한 과제에는 시간을 들여 추론한다. 개발자는 <code class="language-plaintext highlighter-rouge">/effort</code> 파라미터로 이 밸런스를 강제 조정할 수도 있다.</p>

<h3 id="14-압도적인-추론-능력">1.4 압도적인 추론 능력</h3>

<ul>
  <li><strong>ARC AGI 2</strong>: <strong>68.8%</strong> (전작 대비 +31.2%p). “인간에겐 쉽지만 AI에겐 어려운” 추론 영역에서 비약적인 발전을 이뤘다.</li>
  <li><strong>사이버보안</strong>: 레드팀 테스트에서 500개 이상의 제로데이 취약점을 독자적으로 발견했다.</li>
</ul>

<hr />

<h2 id="2-gpt-53-codex-스스로-자신을-만든-ai">2. GPT-5.3-Codex: 스스로 자신을 만든 AI</h2>

<p>OpenAI는 “Codex”라는 브랜드를 통해 코딩 전문 모델의 입지를 굳혀왔다. 5.3 버전은 그 정점에 서 있다.</p>

<h3 id="21-셀프-빌드-self-build">2.1 셀프 빌드 (Self-Build)</h3>

<p>가장 충격적인 변화다. <strong>GPT-5.3-Codex는 자신의 개발 과정에 직접 참여</strong>했다. 훈련 데이터 정제, 디버깅, 배포 관리까지 AI가 수행했다. OpenAI 연구팀조차 “Codex가 자체 개발 가속화에 기여한 정도에 경탄했다”고 밝힐 정도다.</p>

<h3 id="22-코딩과-범용-추론의-통합">2.2 코딩과 범용 추론의 통합</h3>

<p>기존에는 코딩(Codex)과 추론(GPT-n)이 분리되어 있었다면, 5.3은 이를 하나로 합쳤다. 코드를 짜다가 기획서를 쓰고, 다시 엑셀 데이터를 분석하는 흐름이 하나의 에이전트 안에서 매끄럽게 이어진다.</p>

<h3 id="23-터미널의-지배자">2.3 터미널의 지배자</h3>

<ul>
  <li><strong>Terminal-Bench 2.0</strong>: <strong>77.3%</strong> 달성. 경쟁 모델들을 압도하는 수치다. 터미널 환경에서의 복잡한 엔지니어링 작업에 있어 타의 추종을 불허한다.</li>
</ul>

<h3 id="24-속도와-효율">2.4 속도와 효율</h3>

<p>이전 모델 대비 <strong>25% 빨라졌다</strong>. 특히 긴 호흡의 작업(long-running tasks)에서 체감 성능 향상이 뚜렷하며, 비용 효율성도 개선되었다.</p>

<hr />

<h2 id="3-정면-승부-스펙-및-성능-비교">3. 정면 승부: 스펙 및 성능 비교</h2>

<p>두 모델을 나란히 놓고 보면 각자의 지향점이 명확히 드러난다.</p>

<table>
  <thead>
    <tr>
      <th>비교 항목</th>
      <th>Claude Opus 4.6</th>
      <th>GPT-5.3-Codex</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>핵심 철학</strong></td>
      <td>협업하는 에이전트 팀</td>
      <td>스스로 진화하는 통합 엔지니어</td>
    </tr>
    <tr>
      <td><strong>컨텍스트</strong></td>
      <td><strong>100만 토큰</strong></td>
      <td>(기존 유지)</td>
    </tr>
    <tr>
      <td><strong>강점 벤치마크</strong></td>
      <td><strong>ARC AGI 2 (68.8%)</strong>, OSWorld (72.7%)</td>
      <td><strong>Terminal-Bench 2.0 (77.3%)</strong></td>
    </tr>
    <tr>
      <td><strong>주요 신기능</strong></td>
      <td>에이전트 팀, 적응형 사고</td>
      <td>셀프 빌드, Deep Diff, 속도 25%↑</td>
    </tr>
    <tr>
      <td><strong>비즈니스 통합</strong></td>
      <td>PPT 신규 통합, Excel 강화</td>
      <td>유료 ChatGPT 플랜에 포함</td>
    </tr>
    <tr>
      <td><strong>안전성</strong></td>
      <td>제로데이 500+ 발견</td>
      <td>보안 등급 “High” 최초 분류</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="4-이번-출시가-말해주는-것">4. 이번 출시가 말해주는 것</h2>

<h3 id="41-도구의-종말-동료의-시작">4.1 “도구”의 종말, “동료”의 시작</h3>

<p>두 모델 모두 지향하는 바가 같다. 단순히 코드를 “생성”해주는 도구를 넘어, 전체 워크플로우를 주도적으로 이끌어가는 “주체”가 되려 한다. Anthropic의 “에이전트 팀”이나 OpenAI의 “셀프 빌드” 모두 인간의 개입을 최소화하고 AI의 자율성을 극대화하는 방향이다.</p>

<h3 id="42-목적에-따른-선택의-시대">4.2 목적에 따른 선택의 시대</h3>

<p>이제 “어떤 모델이 더 좋아요?”라는 질문은 의미가 없다. 상황에 따라 골라 써야 한다.</p>

<ul>
  <li><strong>Claude Opus 4.6</strong>: 거대한 문서를 분석하거나, 기획-개발-검증이 얽힌 복합 프로젝트를 <strong>병렬</strong>로 처리할 때.</li>
  <li><strong>GPT-5.3-Codex</strong>: 터미널 중심의 하드코어 엔지니어링 작업이나, 빠른 속도와 통합 워크플로우가 필요할 때.</li>
</ul>

<hr />

<h2 id="5-우려와-한계">5. 우려와 한계</h2>

<h3 id="51-퇴보하는-성능들">5.1 퇴보하는 성능들</h3>

<p>흥미롭게도 모든 지표가 우상향은 아니다. Opus 4.6은 SWE-Bench Verified에서 전작보다 소폭 하락했고, GPT-5.3 역시 SWE-Bench Pro에서 유의미한 상승을 보여주지 못했다(+0.4%p). 모델이 복잡해지면서 특정 영역에서는 오히려 효율이 떨어지거나 “생각만 하다가” 엉뚱한 답을 내놓는 경우가 생길 수 있다.</p>

<h3 id="52-블랙박스화-되는-개발-과정">5.2 블랙박스화 되는 개발 과정</h3>

<p>“에이전트 팀”이 알아서 일하고, “셀프 빌드”로 스스로를 만든다. 결과물은 훌륭할지 몰라도, 그 <strong>중간 과정</strong>을 인간이 얼마나 투명하게 이해하고 통제할 수 있을지는 미지수다. 디버깅이 코드를 고치는 게 아니라, AI 에이전트의 “사고 과정”을 디버깅해야 하는 시대로 가고 있다.</p>

<hr />

<h2 id="6-마무리">6. 마무리</h2>

<h3 id="61-요약">6.1 요약</h3>

<table>
  <thead>
    <tr>
      <th>모델</th>
      <th>핵심 키워드</th>
      <th>한 줄 평</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Claude Opus 4.6</strong></td>
      <td>Agent Teams, 1M Context</td>
      <td>“여러 명의 똑똑한 팀원을 한 번에 고용하는 효과”</td>
    </tr>
    <tr>
      <td><strong>GPT-5.3-Codex</strong></td>
      <td>Self-Build, Speed</td>
      <td>“스스로를 설계한, 가장 빠르고 강력한 엔지니어”</td>
    </tr>
  </tbody>
</table>

<h3 id="62-2026년-2월-키워드">6.2 2026년 2월 키워드</h3>

<ol>
  <li><strong>AI Teams</strong>: 단일 에이전트가 아닌, 다중 에이전트 협업 시스템의 대중화.</li>
  <li><strong>Adaptive Compute</strong>: 문제 난이도에 따라 리소스를 조절하는 유연한 AI.</li>
  <li><strong>Self-Evolving AI</strong>: AI가 자신의 다음 버전을 만드는 데 기여하는 순환 구조의 시작.</li>
</ol>

<h3 id="63-앞으로-지켜볼-것">6.3 앞으로 지켜볼 것</h3>

<ul>
  <li><strong>에이전트 오케스트레이션 도구</strong>: Opus의 ‘에이전트 팀’ 개념을 실제 업무 툴(Jira, Slack 등)과 어떻게 매끄럽게 연결할 것인가.</li>
  <li><strong>개발자 생태계의 이동</strong>: 100만 컨텍스트의 Opus와 터미널 최강자 GPT-5.3 사이에서 개발자들이 어떤 도구를 메인으로 선택할지.</li>
</ul>

<hr />

<h2 id="참고-링크">참고 링크</h2>

<ul>
  <li><a href="https://www.anthropic.com/news/opus-4-6">Anthropic - Claude Opus 4.6 발표</a></li>
  <li><a href="https://openai.com/index/gpt-5-3-codex">OpenAI - GPT-5.3-Codex 소개</a></li>
  <li><a href="https://benchmarks.ai/terminal-v2">Benchmark Results - Terminal Bench 2.0</a></li>
</ul>]]></content><author><name>Comad.J</name></author><category term="GEN_AI" /><category term="ai" /><category term="gen_ai" /><category term="anthropic" /><category term="claude" /><category term="openai" /><category term="gpt" /><category term="codex" /><category term="agentic_ai" /><summary type="html"><![CDATA[Claude Opus 4.6 vs GPT-5.3 Codex 심층 비교]]></summary></entry><entry><title type="html">[Python] Plotly 인터랙티브: 모드바, 기본차트, 슬라이더</title><link href="https://kinkos1234.github.io/coding/Plotly_5/" rel="alternate" type="text/html" title="[Python] Plotly 인터랙티브: 모드바, 기본차트, 슬라이더" /><published>2026-02-05T00:00:00+09:00</published><updated>2026-02-05T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/Plotly_5</id><content type="html" xml:base="https://kinkos1234.github.io/coding/Plotly_5/"><![CDATA[<h1 id="plotly-인터랙티브-요소-모드바부터-슬라이더까지">Plotly 인터랙티브 요소: 모드바부터 슬라이더까지</h1>

<p>오늘은 Plotly의 인터랙티브 기능들을 본격적으로 다뤄봤다. 단순히 그래프를 그리는 것을 넘어서, 사용자가 직접 데이터를 조작하고 탐색할 수 있는 환경을 만드는 게 목표였다.</p>

<p>실습 데이터로는 <strong>Kaggle Netflix Movies and TV Shows 데이터셋</strong>을 사용했다.</p>

<hr />

<h2 id="0-데이터-준비">0. 데이터 준비</h2>

<p>Netflix 데이터는 인코딩 문제가 있어서 <code class="language-plaintext highlighter-rouge">latin1</code>으로 읽어야 한다. 그리고 불필요한 빈 컬럼들이 많아서 <code class="language-plaintext highlighter-rouge">dropna()</code>로 한 번에 정리해줬다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">plotly.graph_objects</span> <span class="k">as</span> <span class="n">go</span>

<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">'netflix_titles.csv'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">'latin1'</span><span class="p">)</span>

<span class="c1"># 비어있는 컬럼 전부 제거
</span><span class="n">df</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">dropna</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">how</span><span class="o">=</span><span class="s">'all'</span><span class="p">)</span>
</code></pre></div></div>

<p>처음에 26개였던 컬럼이 12개로 줄었다. 쓸모없는 컬럼을 미리 정리해두면 이후 작업이 훨씬 깔끔해진다.</p>

<hr />

<h2 id="1-모드-바mode-bar-커스터마이징">1. 모드 바(Mode Bar) 커스터마이징</h2>

<p>Plotly 그래프 우측 상단에는 기본적으로 줌, 이동, 저장 등의 버튼이 있는 툴바가 있다. 이걸 <strong>모드 바(Mode Bar)</strong>라고 한다.</p>

<p><code class="language-plaintext highlighter-rouge">fig.show(config={...})</code>로 모드 바를 원하는 대로 조정할 수 있다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">year_count</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">groupby</span><span class="p">(</span><span class="s">'release_year'</span><span class="p">)[</span><span class="s">'title'</span><span class="p">].</span><span class="n">count</span><span class="p">().</span><span class="n">reset_index</span><span class="p">()</span>

<span class="n">fig</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>
<span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Bar</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="n">year_count</span><span class="p">[</span><span class="s">'release_year'</span><span class="p">],</span> <span class="n">y</span><span class="o">=</span><span class="n">year_count</span><span class="p">[</span><span class="s">'title'</span><span class="p">]))</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">(</span><span class="n">config</span><span class="o">=</span><span class="p">{</span>
    <span class="s">'displayModeBar'</span><span class="p">:</span> <span class="bp">True</span><span class="p">,</span>                           <span class="c1"># 모드 바 표시
</span>    <span class="s">'displaylogo'</span><span class="p">:</span> <span class="bp">False</span><span class="p">,</span>                             <span class="c1"># Plotly 로고 숨기기
</span>    <span class="s">'modeBarButtonsToRemove'</span><span class="p">:</span> <span class="p">[</span><span class="s">'toImage'</span><span class="p">,</span> <span class="s">'zoom2d'</span><span class="p">]</span>  <span class="c1"># 특정 버튼 제거
</span><span class="p">})</span>
</code></pre></div></div>

<p>제거할 수 있는 버튼 종류는 <code class="language-plaintext highlighter-rouge">zoom2d</code>, <code class="language-plaintext highlighter-rouge">pan2d</code>, <code class="language-plaintext highlighter-rouge">zoomIn2d</code>, <code class="language-plaintext highlighter-rouge">zoomOut2d</code>, <code class="language-plaintext highlighter-rouge">toImage</code> 등이 있다. 불필요한 버튼을 제거하면 사용자 경험이 훨씬 깔끔해진다.</p>

<hr />

<h2 id="2-기본-차트-3종-bar-scatter-line">2. 기본 차트 3종 (Bar, Scatter, Line)</h2>

<h3 id="21-bar-차트">2.1 Bar 차트</h3>

<p>연도별 Netflix 콘텐츠 수를 bar 차트로 그려봤다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">year_count</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">groupby</span><span class="p">(</span><span class="s">'release_year'</span><span class="p">)[</span><span class="s">'title'</span><span class="p">].</span><span class="n">count</span><span class="p">().</span><span class="n">reset_index</span><span class="p">()</span>

<span class="n">fig</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>
<span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Bar</span><span class="p">(</span>
    <span class="n">x</span><span class="o">=</span><span class="n">year_count</span><span class="p">[</span><span class="s">'release_year'</span><span class="p">],</span>
    <span class="n">y</span><span class="o">=</span><span class="n">year_count</span><span class="p">[</span><span class="s">'title'</span><span class="p">]</span>
<span class="p">))</span>
<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="22-scatter-차트">2.2 Scatter 차트</h3>

<p>Movie와 TV Show를 각각 다른 trace로 표현했다. 필터링 후 각각 변수에 저장하고 trace를 두 개 추가하는 방식이다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">type_year</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">groupby</span><span class="p">([</span><span class="s">'release_year'</span><span class="p">,</span> <span class="s">'type'</span><span class="p">])[</span><span class="s">'title'</span><span class="p">].</span><span class="n">count</span><span class="p">().</span><span class="n">reset_index</span><span class="p">()</span>

<span class="n">fig2</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>

<span class="n">fig2</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span>
    <span class="n">x</span><span class="o">=</span><span class="n">type_year</span><span class="p">[</span><span class="n">type_year</span><span class="p">[</span><span class="s">'type'</span><span class="p">]</span><span class="o">==</span><span class="s">'Movie'</span><span class="p">][</span><span class="s">'release_year'</span><span class="p">],</span>
    <span class="n">y</span><span class="o">=</span><span class="n">type_year</span><span class="p">[</span><span class="n">type_year</span><span class="p">[</span><span class="s">'type'</span><span class="p">]</span><span class="o">==</span><span class="s">'Movie'</span><span class="p">][</span><span class="s">'title'</span><span class="p">],</span>
    <span class="n">name</span><span class="o">=</span><span class="s">'Movie'</span>
<span class="p">))</span>

<span class="n">fig2</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span>
    <span class="n">x</span><span class="o">=</span><span class="n">type_year</span><span class="p">[</span><span class="n">type_year</span><span class="p">[</span><span class="s">'type'</span><span class="p">]</span><span class="o">==</span><span class="s">'TV Show'</span><span class="p">][</span><span class="s">'release_year'</span><span class="p">],</span>
    <span class="n">y</span><span class="o">=</span><span class="n">type_year</span><span class="p">[</span><span class="n">type_year</span><span class="p">[</span><span class="s">'type'</span><span class="p">]</span><span class="o">==</span><span class="s">'TV Show'</span><span class="p">][</span><span class="s">'title'</span><span class="p">],</span>
    <span class="n">name</span><span class="o">=</span><span class="s">'TV Show'</span>
<span class="p">))</span>

<span class="n">fig2</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="23-line-차트">2.3 Line 차트</h3>

<p>Scatter에서 <code class="language-plaintext highlighter-rouge">mode='lines'</code>만 추가하면 line 차트가 된다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig3</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>

<span class="n">fig3</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span>
    <span class="n">x</span><span class="o">=</span><span class="n">type_year</span><span class="p">[</span><span class="n">type_year</span><span class="p">[</span><span class="s">'type'</span><span class="p">]</span><span class="o">==</span><span class="s">'Movie'</span><span class="p">][</span><span class="s">'release_year'</span><span class="p">],</span>
    <span class="n">y</span><span class="o">=</span><span class="n">type_year</span><span class="p">[</span><span class="n">type_year</span><span class="p">[</span><span class="s">'type'</span><span class="p">]</span><span class="o">==</span><span class="s">'Movie'</span><span class="p">][</span><span class="s">'title'</span><span class="p">],</span>
    <span class="n">name</span><span class="o">=</span><span class="s">'Movie'</span><span class="p">,</span>
    <span class="n">mode</span><span class="o">=</span><span class="s">'lines'</span>
<span class="p">))</span>

<span class="n">fig3</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span>
    <span class="n">x</span><span class="o">=</span><span class="n">type_year</span><span class="p">[</span><span class="n">type_year</span><span class="p">[</span><span class="s">'type'</span><span class="p">]</span><span class="o">==</span><span class="s">'TV Show'</span><span class="p">][</span><span class="s">'release_year'</span><span class="p">],</span>
    <span class="n">y</span><span class="o">=</span><span class="n">type_year</span><span class="p">[</span><span class="n">type_year</span><span class="p">[</span><span class="s">'type'</span><span class="p">]</span><span class="o">==</span><span class="s">'TV Show'</span><span class="p">][</span><span class="s">'title'</span><span class="p">],</span>
    <span class="n">name</span><span class="o">=</span><span class="s">'TV Show'</span><span class="p">,</span>
    <span class="n">mode</span><span class="o">=</span><span class="s">'lines'</span>
<span class="p">))</span>

<span class="n">fig3</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<hr />

<h2 id="3-scientific-chart-heatmap">3. Scientific Chart: Heatmap</h2>

<p>Plotly 공식문서의 Scientific Charts 섹션에서 Heatmap을 골랐다. 연도별 x 콘텐츠 타입별 콘텐츠 수를 색깔로 표현하기에 딱 맞는 차트다.</p>

<p>먼저 <code class="language-plaintext highlighter-rouge">pivot()</code>으로 데이터를 Heatmap에 맞는 형태로 변환한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">heatmap_data</span> <span class="o">=</span> <span class="n">type_year</span><span class="p">.</span><span class="n">pivot</span><span class="p">(</span>
    <span class="n">index</span><span class="o">=</span><span class="s">'type'</span><span class="p">,</span>
    <span class="n">columns</span><span class="o">=</span><span class="s">'release_year'</span><span class="p">,</span>
    <span class="n">values</span><span class="o">=</span><span class="s">'title'</span>
<span class="p">)</span>

<span class="n">fig</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>

<span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Heatmap</span><span class="p">(</span>
    <span class="n">x</span><span class="o">=</span><span class="n">heatmap_data</span><span class="p">.</span><span class="n">columns</span><span class="p">,</span>  <span class="c1"># 연도
</span>    <span class="n">y</span><span class="o">=</span><span class="n">heatmap_data</span><span class="p">.</span><span class="n">index</span><span class="p">,</span>    <span class="c1"># Movie / TV Show
</span>    <span class="n">z</span><span class="o">=</span><span class="n">heatmap_data</span><span class="p">.</span><span class="n">values</span><span class="p">,</span>   <span class="c1"># 콘텐츠 수 (색깔값)
</span>    <span class="n">colorscale</span><span class="o">=</span><span class="s">'aggrnyl'</span>     <span class="c1"># 공식문서에서 선택한 색상 스케일
</span><span class="p">))</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p>colorscale은 <a href="https://plotly.com/python/colorscales/">Plotly 공식문서</a>에서 원하는 걸 골라서 이름만 넣으면 된다. 2015년 이후 콘텐츠가 급격히 늘어난 게 색깔로 확연히 드러난다.</p>

<hr />

<h2 id="4-슬라이더-slider">4. 슬라이더 (Slider)</h2>

<p>슬라이더란 <strong>전체 범주 중 특정 범위의 값을 조정하여 원하는 시점의 데이터만 볼 수 있게 해주는 기능</strong>이다. Plotly에서는 두 가지 방식으로 구현할 수 있다.</p>

<h3 id="41-싱글-슬라이더">4.1 싱글 슬라이더</h3>

<p>처음 개념을 이해하는 게 가장 어려웠다. 핵심은 이렇다:</p>

<blockquote>
  <p>모든 연도의 trace를 미리 다 그려두고, 전부 숨겨놓은 뒤, 슬라이더로 선택한 연도의 trace만 보이게 한다.</p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df_recent</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">'release_year'</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="mi">2010</span><span class="p">]</span>
<span class="n">years_recent</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">df_recent</span><span class="p">[</span><span class="s">'release_year'</span><span class="p">].</span><span class="n">unique</span><span class="p">())</span>

<span class="n">fig</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>

<span class="c1"># 1단계: 연도별 trace를 전부 추가 (숨김 상태)
</span><span class="k">for</span> <span class="n">year</span> <span class="ow">in</span> <span class="n">years_recent</span><span class="p">:</span>
    <span class="n">year_data</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">'release_year'</span><span class="p">]</span> <span class="o">==</span> <span class="n">year</span><span class="p">]</span>
    <span class="n">count</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">year_data</span><span class="p">)</span>
    <span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Bar</span><span class="p">(</span>
        <span class="n">x</span><span class="o">=</span><span class="p">[</span><span class="n">year</span><span class="p">],</span>
        <span class="n">y</span><span class="o">=</span><span class="p">[</span><span class="n">count</span><span class="p">],</span>
        <span class="n">name</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">year</span><span class="p">),</span>
        <span class="n">visible</span><span class="o">=</span><span class="bp">False</span>  <span class="c1"># 전부 숨김
</span>    <span class="p">))</span>

<span class="c1"># 2단계: 각 연도별 step 생성
</span><span class="n">steps</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">year</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">years_recent</span><span class="p">):</span>
    <span class="n">step</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span>
        <span class="n">method</span><span class="o">=</span><span class="s">'update'</span><span class="p">,</span>
        <span class="n">label</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">year</span><span class="p">),</span>
        <span class="n">args</span><span class="o">=</span><span class="p">[{</span><span class="s">'visible'</span><span class="p">:</span> <span class="p">[</span><span class="n">j</span> <span class="o">==</span> <span class="n">i</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">years_recent</span><span class="p">))]}]</span>
    <span class="p">)</span>
    <span class="n">steps</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">step</span><span class="p">)</span>

<span class="c1"># 3단계: 슬라이더 추가
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_layout</span><span class="p">(</span><span class="n">sliders</span><span class="o">=</span><span class="p">[</span><span class="nb">dict</span><span class="p">(</span><span class="n">steps</span><span class="o">=</span><span class="n">steps</span><span class="p">)])</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">[j == i for j in range(len(years_recent))]</code> 이 부분이 핵심인데, 선택한 연도(i)에 해당하는 trace만 <code class="language-plaintext highlighter-rouge">True</code>, 나머지는 전부 <code class="language-plaintext highlighter-rouge">False</code>로 만들어주는 리스트다.</p>

<h3 id="42-더블-슬라이더-range-slider">4.2 더블 슬라이더 (Range Slider)</h3>

<p>싱글 슬라이더보다 훨씬 간단하다. <code class="language-plaintext highlighter-rouge">rangeslider_visible=True</code> 한 줄이면 된다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig3</span><span class="p">.</span><span class="n">update_xaxes</span><span class="p">(</span>
    <span class="n">rangeslider_visible</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
    <span class="n">rangeslider</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span>
        <span class="n">bgcolor</span><span class="o">=</span><span class="s">'lightgray'</span><span class="p">,</span>  <span class="c1"># 슬라이더 배경색
</span>        <span class="n">thickness</span><span class="o">=</span><span class="mf">0.3</span>         <span class="c1"># 슬라이더 두께 (0~1)
</span>    <span class="p">)</span>
<span class="p">)</span>

<span class="n">fig3</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p>그래프 하단에 범위를 드래그해서 원하는 구간만 확대해서 볼 수 있다. 시계열 데이터를 탐색할 때 특히 유용하다.</p>

<hr />

<h2 id="5-정리">5. 정리</h2>

<table>
  <thead>
    <tr>
      <th>기능</th>
      <th>메서드/옵션</th>
      <th>용도</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>모드 바 설정</td>
      <td><code class="language-plaintext highlighter-rouge">fig.show(config={...})</code></td>
      <td>툴바 버튼 추가/제거</td>
    </tr>
    <tr>
      <td>로고 숨기기</td>
      <td><code class="language-plaintext highlighter-rouge">displaylogo=False</code></td>
      <td>Plotly 로고 제거</td>
    </tr>
    <tr>
      <td>버튼 제거</td>
      <td><code class="language-plaintext highlighter-rouge">modeBarButtonsToRemove</code></td>
      <td>특정 버튼 숨기기</td>
    </tr>
    <tr>
      <td>Heatmap</td>
      <td><code class="language-plaintext highlighter-rouge">go.Heatmap()</code></td>
      <td>2D 색깔 격자 차트</td>
    </tr>
    <tr>
      <td>색상 스케일</td>
      <td><code class="language-plaintext highlighter-rouge">colorscale='...'</code></td>
      <td>Heatmap 색상 변경</td>
    </tr>
    <tr>
      <td>싱글 슬라이더</td>
      <td><code class="language-plaintext highlighter-rouge">sliders=[dict(steps=...)]</code></td>
      <td>특정 값 선택</td>
    </tr>
    <tr>
      <td>더블 슬라이더</td>
      <td><code class="language-plaintext highlighter-rouge">rangeslider_visible=True</code></td>
      <td>범위 선택</td>
    </tr>
  </tbody>
</table>

<p>슬라이더 개념을 처음 접했을 때 “모든 trace를 미리 다 그려두고 숨긴다”는 발상이 낯설었다. 근데 이해하고 나니까 왜 이렇게 구현하는지 납득이 됐다. 인터랙티브 요소는 결국 사용자에게 데이터 탐색의 자유를 주는 것이고, 그 자유를 구현하는 방식이 이런 구조인 것이다.</p>

<hr />

<p><strong>Reference</strong></p>

<ul>
  <li>Dataset: <a href="https://www.kaggle.com/datasets/shivamb/netflix-shows">Kaggle Netflix Movies and TV Shows</a></li>
  <li>Plotly Colorscales: <a href="https://plotly.com/python/colorscales/">https://plotly.com/python/colorscales/</a></li>
</ul>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Python" /><category term="Plotly" /><category term="Interactive" /><category term="Slider" /><category term="Heatmap" /><summary type="html"><![CDATA[Plotly 인터랙티브 요소: 모드바부터 슬라이더까지]]></summary></entry><entry><title type="html">[Python] Plotly 상호작용: 버튼과 드롭다운 (updatemenus)</title><link href="https://kinkos1234.github.io/coding/Plotly_4/" rel="alternate" type="text/html" title="[Python] Plotly 상호작용: 버튼과 드롭다운 (updatemenus)" /><published>2026-02-04T00:00:00+09:00</published><updated>2026-02-04T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/Plotly_4</id><content type="html" xml:base="https://kinkos1234.github.io/coding/Plotly_4/"><![CDATA[<h1 id="plotly-인터랙티브-요소-버튼과-드롭다운으로-대시보드-만들기">Plotly 인터랙티브 요소: 버튼과 드롭다운으로 대시보드 만들기</h1>

<p>데이터 시각화의 꽃은 뭐니 뭐니 해도 사용자가 직접 데이터를 탐색할 수 있는 <strong>인터랙티브(Interactive)</strong> 요소라고 생각한다. 오늘은 <strong>Kaggle World Happiness Report</strong> 데이터를 활용해서, Plotly의 <code class="language-plaintext highlighter-rouge">updatemenus</code> 기능으로 <strong>버튼과 드롭다운</strong>을 구현하고 이를 세밀하게 튜닝하는 방법을 정리해 봤다.</p>

<hr />

<h2 id="1-데이터-준비-및-설정">1. 데이터 준비 및 설정</h2>

<p>먼저 데이터를 불러오고 분석할 지표들을 정의한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">plotly.graph_objects</span> <span class="k">as</span> <span class="n">go</span>

<span class="c1"># 데이터 로드
</span><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">'world_happiness_report.csv'</span><span class="p">)</span>

<span class="c1"># 1. 설정: 분석할 연도와 지표 정의
</span><span class="n">years</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">df</span><span class="p">[</span><span class="s">'Year'</span><span class="p">].</span><span class="n">unique</span><span class="p">(),</span> <span class="n">reverse</span><span class="o">=</span><span class="bp">True</span><span class="p">)[:</span><span class="mi">5</span><span class="p">]</span>  <span class="c1"># 최근 5개년
</span><span class="n">metrics</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'Happiness_Score'</span><span class="p">:</span> <span class="s">'행복 점수'</span><span class="p">,</span>
    <span class="s">'Healthy_Life_Expectancy'</span><span class="p">:</span> <span class="s">'기대 수명'</span><span class="p">,</span>
    <span class="s">'Social_Support'</span><span class="p">:</span> <span class="s">'사회적 지지'</span>
<span class="p">}</span>
<span class="n">metric_keys</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">metrics</span><span class="p">.</span><span class="n">keys</span><span class="p">())</span>
</code></pre></div></div>

<hr />

<h2 id="2-초기-그래프-생성">2. 초기 그래프 생성</h2>

<p>드롭다운을 만들기 전에, 가장 기본이 되는 첫 화면 그래프를 그려야 한다. 초기 연도와 초기 지표를 선택해서 산점도(Scatter Plot)를 그린다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">initial_year</span> <span class="o">=</span> <span class="n">years</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">initial_metric</span> <span class="o">=</span> <span class="n">metric_keys</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">curr_df</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">'Year'</span><span class="p">]</span> <span class="o">==</span> <span class="n">initial_year</span><span class="p">]</span>

<span class="n">fig</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>

<span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span>
    <span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span>
        <span class="n">x</span><span class="o">=</span><span class="n">curr_df</span><span class="p">[</span><span class="s">'GDP_per_Capita'</span><span class="p">],</span>
        <span class="n">y</span><span class="o">=</span><span class="n">curr_df</span><span class="p">[</span><span class="n">initial_metric</span><span class="p">],</span>
        <span class="n">mode</span><span class="o">=</span><span class="s">'markers'</span><span class="p">,</span>
        <span class="n">text</span><span class="o">=</span><span class="n">curr_df</span><span class="p">[</span><span class="s">'Country'</span><span class="p">],</span>
        <span class="n">marker</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span>
            <span class="n">size</span><span class="o">=</span><span class="mi">14</span><span class="p">,</span>
            <span class="n">color</span><span class="o">=</span><span class="n">curr_df</span><span class="p">[</span><span class="n">initial_metric</span><span class="p">],</span>
            <span class="n">colorscale</span><span class="o">=</span><span class="s">'Electric'</span><span class="p">,</span>
            <span class="n">showscale</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
            <span class="n">line</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">width</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'White'</span><span class="p">)</span>
        <span class="p">),</span>
        <span class="n">name</span><span class="o">=</span><span class="n">metrics</span><span class="p">[</span><span class="n">initial_metric</span><span class="p">]</span>
    <span class="p">)</span>
<span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="3-드롭다운dropdown-구현-연도-선택">3. 드롭다운(Dropdown) 구현: 연도 선택</h2>

<p><code class="language-plaintext highlighter-rouge">updatemenus</code>의 핵심은 <code class="language-plaintext highlighter-rouge">method="update"</code>다. 버튼을 클릭했을 때 데이터(<code class="language-plaintext highlighter-rouge">x</code>, <code class="language-plaintext highlighter-rouge">y</code>, <code class="language-plaintext highlighter-rouge">text</code>)를 새 데이터로 갈아끼우는 방식이다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">year_options</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">year</span> <span class="ow">in</span> <span class="n">years</span><span class="p">:</span>
    <span class="n">year_df</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">'Year'</span><span class="p">]</span> <span class="o">==</span> <span class="n">year</span><span class="p">]</span>
    <span class="n">year_options</span><span class="p">.</span><span class="n">append</span><span class="p">(</span>
        <span class="nb">dict</span><span class="p">(</span>
            <span class="n">method</span><span class="o">=</span><span class="s">"update"</span><span class="p">,</span>
            <span class="n">label</span><span class="o">=</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">year</span><span class="si">}</span><span class="s">년"</span><span class="p">,</span>  <span class="c1"># 라벨에 이모지 추가로 직관성 UP
</span>            <span class="n">args</span><span class="o">=</span><span class="p">[{</span>
                <span class="s">"x"</span><span class="p">:</span> <span class="p">[</span><span class="n">year_df</span><span class="p">[</span><span class="s">'GDP_per_Capita'</span><span class="p">]],</span>
                <span class="s">"y"</span><span class="p">:</span> <span class="p">[</span><span class="n">year_df</span><span class="p">[</span><span class="n">initial_metric</span><span class="p">]],</span>
                <span class="s">"text"</span><span class="p">:</span> <span class="p">[</span><span class="n">year_df</span><span class="p">[</span><span class="s">'Country'</span><span class="p">]]</span>
            <span class="p">}]</span>
        <span class="p">)</span>
    <span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="4-버튼button-구현-지표-전환">4. 버튼(Button) 구현: 지표 전환</h2>

<p>이번엔 탭(Tab) 메뉴처럼 생긴 버튼을 만들어보자. 여기서는 단순히 데이터만 바꾸는 게 아니라, <strong>축 제목</strong>과 <strong>차트 제목</strong>까지 동적으로 변경해야 한다. <code class="language-plaintext highlighter-rouge">args</code>의 두 번째 인자로 레이아웃 업데이트 정보를 넘겨주면 된다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">metric_buttons</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">metrics</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
    <span class="n">metric_buttons</span><span class="p">.</span><span class="n">append</span><span class="p">(</span>
        <span class="nb">dict</span><span class="p">(</span>
            <span class="n">method</span><span class="o">=</span><span class="s">"update"</span><span class="p">,</span>
            <span class="n">label</span><span class="o">=</span><span class="n">name</span><span class="p">,</span>
            <span class="n">args</span><span class="o">=</span><span class="p">[</span>
                <span class="p">{</span><span class="s">"y"</span><span class="p">:</span> <span class="p">[</span><span class="n">curr_df</span><span class="p">[</span><span class="n">key</span><span class="p">]],</span> <span class="s">"marker.color"</span><span class="p">:</span> <span class="p">[</span><span class="n">curr_df</span><span class="p">[</span><span class="n">key</span><span class="p">]]},</span>  <span class="c1"># 데이터 변경
</span>                <span class="p">{</span><span class="s">"yaxis"</span><span class="p">:</span> <span class="p">{</span><span class="s">"title"</span><span class="p">:</span> <span class="n">name</span><span class="p">},</span> <span class="s">"title"</span><span class="p">:</span> <span class="sa">f</span><span class="s">"&lt;b&gt;GDP 대비 </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s"> 상관관계&lt;/b&gt;"</span><span class="p">}</span>  <span class="c1"># 레이아웃 변경
</span>            <span class="p">]</span>
        <span class="p">)</span>
    <span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="5-레이아웃-튜닝-uiux-최적화">5. 레이아웃 튜닝 (UI/UX 최적화)</h2>

<p>이제 만든 메뉴들을 적절한 위치에 배치해야 한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span><span class="p">.</span><span class="n">update_layout</span><span class="p">(</span>
    <span class="n">template</span><span class="o">=</span><span class="s">"plotly_dark"</span><span class="p">,</span>
    <span class="n">title</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="sa">f</span><span class="s">"&lt;b&gt;GDP 대비 </span><span class="si">{</span><span class="n">metrics</span><span class="p">[</span><span class="n">initial_metric</span><span class="p">]</span><span class="si">}</span><span class="s"> 상관관계&lt;/b&gt;"</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">font</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">20</span><span class="p">)),</span>
    <span class="n">xaxis_title</span><span class="o">=</span><span class="s">"1인당 GDP (Purchasing Power)"</span><span class="p">,</span>
    <span class="n">yaxis_title</span><span class="o">=</span><span class="n">metrics</span><span class="p">[</span><span class="n">initial_metric</span><span class="p">],</span>
    <span class="n">margin</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">t</span><span class="o">=</span><span class="mi">120</span><span class="p">),</span>  <span class="c1"># 상단 여백 확보 (메뉴 공간)
</span>
    <span class="n">updatemenus</span><span class="o">=</span><span class="p">[</span>
        <span class="c1"># 1. 드롭다운 배치 (상단 왼쪽)
</span>        <span class="nb">dict</span><span class="p">(</span>
            <span class="nb">type</span><span class="o">=</span><span class="s">"dropdown"</span><span class="p">,</span>
            <span class="n">buttons</span><span class="o">=</span><span class="n">year_options</span><span class="p">,</span>
            <span class="n">direction</span><span class="o">=</span><span class="s">"down"</span><span class="p">,</span>
            <span class="n">showactive</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
            <span class="n">x</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">xanchor</span><span class="o">=</span><span class="s">"left"</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mf">1.2</span><span class="p">,</span> <span class="n">yanchor</span><span class="o">=</span><span class="s">"top"</span>
        <span class="p">),</span>
        <span class="c1"># 2. 버튼 배치 (상단 오른쪽)
</span>        <span class="nb">dict</span><span class="p">(</span>
            <span class="nb">type</span><span class="o">=</span><span class="s">"buttons"</span><span class="p">,</span>
            <span class="n">buttons</span><span class="o">=</span><span class="n">metric_buttons</span><span class="p">,</span>
            <span class="n">direction</span><span class="o">=</span><span class="s">"right"</span><span class="p">,</span>
            <span class="n">x</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">xanchor</span><span class="o">=</span><span class="s">"right"</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mf">1.2</span><span class="p">,</span> <span class="n">yanchor</span><span class="o">=</span><span class="s">"top"</span><span class="p">,</span>
            <span class="n">bgcolor</span><span class="o">=</span><span class="s">"rgba(255, 255, 255, 0.1)"</span><span class="p">,</span>  <span class="c1"># 투명도 있는 배경
</span>            <span class="n">font</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">color</span><span class="o">=</span><span class="s">"white"</span><span class="p">)</span>
        <span class="p">),</span>
    <span class="p">]</span>
<span class="p">)</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">margin</code>: 메뉴가 차트 제목을 가리지 않도록 상단 여백(<code class="language-plaintext highlighter-rouge">t=120</code>)을 넉넉히 준다.</li>
  <li><code class="language-plaintext highlighter-rouge">xanchor</code>, <code class="language-plaintext highlighter-rouge">yanchor</code>: 드롭다운은 왼쪽 끝, 버튼은 오른쪽 끝에 배치하여 균형을 맞췄다.</li>
</ul>

<hr />

<h2 id="6-정리">6. 정리</h2>

<p>Plotly의 <code class="language-plaintext highlighter-rouge">updatemenus</code>는 자바스크립트를 몰라도 파이썬만으로 훌륭한 인터랙티브 대시보드를 만들 수 있게 해준다.</p>

<table>
  <thead>
    <tr>
      <th>기능</th>
      <th>속성/값</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>메뉴 타입</td>
      <td><code class="language-plaintext highlighter-rouge">type="dropdown"</code> / <code class="language-plaintext highlighter-rouge">"buttons"</code></td>
      <td>드롭다운 메뉴 또는 버튼 그룹 선택</td>
    </tr>
    <tr>
      <td>동작 방식</td>
      <td><code class="language-plaintext highlighter-rouge">method="update"</code></td>
      <td>데이터와 레이아웃을 동시에 변경</td>
    </tr>
    <tr>
      <td>데이터 변경</td>
      <td><code class="language-plaintext highlighter-rouge">args=[{data_dict}, ...]</code></td>
      <td>첫 번째 인자로 트레이스 데이터 변경 (<code class="language-plaintext highlighter-rouge">x</code>, <code class="language-plaintext highlighter-rouge">y</code>, <code class="language-plaintext highlighter-rouge">marker.color</code> 등)</td>
    </tr>
    <tr>
      <td>레이아웃 변경</td>
      <td><code class="language-plaintext highlighter-rouge">args=[..., {layout_dict}]</code></td>
      <td>두 번째 인자로 레이아웃 속성 변경 (<code class="language-plaintext highlighter-rouge">title</code>, <code class="language-plaintext highlighter-rouge">yaxis</code> 등)</td>
    </tr>
    <tr>
      <td>배치</td>
      <td><code class="language-plaintext highlighter-rouge">x</code>, <code class="language-plaintext highlighter-rouge">y</code>, <code class="language-plaintext highlighter-rouge">xanchor</code>, <code class="language-plaintext highlighter-rouge">yanchor</code></td>
      <td>메뉴의 위치 지정 (0~1 좌표계)</td>
    </tr>
  </tbody>
</table>

<p>단순히 정적인 그래프를 보여주는 것보다, 사용자가 직접 연도를 바꾸고 지표를 바꿔가며 데이터를 탐색하게 하면 분석의 깊이가 달라진다. 다음에는 슬라이더(Slider)를 연동해서 시계열 변화를 애니메이션으로 보여주는 것도 시도해봐야겠다.</p>

<hr />

<p><strong>Reference</strong></p>

<ul>
  <li>Dataset: <a href="https://www.kaggle.com/datasets/khushikyad001/world-happiness-report">Kaggle World Happiness Report</a></li>
</ul>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Python" /><category term="Plotly" /><category term="Interactive" /><category term="Dropdown" /><category term="Button" /><summary type="html"><![CDATA[Plotly 인터랙티브 요소: 버튼과 드롭다운으로 대시보드 만들기]]></summary></entry><entry><title type="html">[Python] Plotly 시각화 마스터: 범례, 템플릿, Pandas 백엔드</title><link href="https://kinkos1234.github.io/coding/Plotly_3/" rel="alternate" type="text/html" title="[Python] Plotly 시각화 마스터: 범례, 템플릿, Pandas 백엔드" /><published>2026-02-03T00:00:00+09:00</published><updated>2026-02-03T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/Plotly_3</id><content type="html" xml:base="https://kinkos1234.github.io/coding/Plotly_3/"><![CDATA[<h1 id="plotly-마스터하기-디테일과-편의성-잡기">Plotly 마스터하기: 디테일과 편의성 잡기</h1>

<p>지난 포스팅들에서는 그래프를 그리고 축을 다루는 법을 익혔다. 오늘은 시각화의 완성도를 높여주는 <strong>범례(Legend)</strong>와 <strong>템플릿(Template)</strong>, 그리고 Pandas에서 Plotly를 바로 쓸 수 있게 해주는 <strong>Backend 설정</strong>까지 실습해봤다.</p>

<p>데이터는 중고차 가격 데이터인 <code class="language-plaintext highlighter-rouge">bmw.csv</code>를 사용했다.</p>

<hr />

<h2 id="1-데이터-준비">1. 데이터 준비</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">plotly.express</span> <span class="k">as</span> <span class="n">px</span>

<span class="n">bmw</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">"bmw.csv"</span><span class="p">)</span>

<span class="c1"># 데이터 확인
</span><span class="k">print</span><span class="p">(</span><span class="n">bmw</span><span class="p">.</span><span class="n">shape</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">bmw</span><span class="p">[</span><span class="s">'model'</span><span class="p">].</span><span class="n">unique</span><span class="p">()[:</span><span class="mi">5</span><span class="p">])</span>
</code></pre></div></div>

<p>모델(model)과 변속기(transmission) 종류에 따른 가격 차이를 시각화해보자. 먼저 집계 데이터를 만든다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 각 모델/변속기별 평균 가격 집계
</span><span class="n">average_prices</span> <span class="o">=</span> <span class="n">bmw</span><span class="p">.</span><span class="n">groupby</span><span class="p">([</span><span class="s">'model'</span><span class="p">,</span> <span class="s">'transmission'</span><span class="p">])[</span><span class="s">'price'</span><span class="p">].</span><span class="n">mean</span><span class="p">().</span><span class="n">reset_index</span><span class="p">()</span>
</code></pre></div></div>

<hr />

<h2 id="2-범례legend-커스터마이징">2. 범례(Legend) 커스터마이징</h2>

<p>기본 범례는 오른쪽에 위치하지만, 그래프 공간을 넓게 쓰거나 디자인 요소를 더하고 싶을 때 위치를 바꿀 수 있다. <code class="language-plaintext highlighter-rouge">update_layout</code>의 <code class="language-plaintext highlighter-rouge">legend</code> 속성을 건드리면 된다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span> <span class="o">=</span> <span class="n">px</span><span class="p">.</span><span class="n">bar</span><span class="p">(</span><span class="n">average_prices</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="s">'model'</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s">'price'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'transmission'</span><span class="p">,</span> <span class="n">title</span><span class="o">=</span><span class="s">'모델 변속기별 평균 가격'</span><span class="p">)</span>

<span class="c1"># 그래프 레이아웃 상세 항목 설정
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_layout</span><span class="p">(</span>
    <span class="n">xaxis_title</span><span class="o">=</span><span class="s">'자동차 모델'</span><span class="p">,</span>
    <span class="n">yaxis_title</span><span class="o">=</span><span class="s">'평균 가격'</span><span class="p">,</span>
    
    <span class="c1"># 범례 커스텀
</span>    <span class="n">legend_title</span><span class="o">=</span><span class="s">'변속기'</span><span class="p">,</span>
    <span class="n">legend_orientation</span><span class="o">=</span><span class="s">"h"</span><span class="p">,</span>       <span class="c1"># 가로(Horizontal) 배치
</span>    <span class="n">legend_yanchor</span><span class="o">=</span><span class="s">"bottom"</span><span class="p">,</span>
    <span class="n">legend_y</span><span class="o">=</span><span class="mf">1.1</span><span class="p">,</span>                 <span class="c1"># 그래프 상단 바깥으로 이동
</span>    <span class="n">legend_xanchor</span><span class="o">=</span><span class="s">"left"</span><span class="p">,</span>
    <span class="n">legend_x</span><span class="o">=</span><span class="mf">0.6</span><span class="p">,</span>
    <span class="n">legend_bgcolor</span><span class="o">=</span><span class="s">"LightGray"</span><span class="p">,</span>   <span class="c1"># 배경색
</span>    <span class="n">legend_bordercolor</span><span class="o">=</span><span class="s">"Gray"</span><span class="p">,</span>    <span class="c1"># 테두리 색
</span>    <span class="n">legend_borderwidth</span><span class="o">=</span><span class="mi">2</span>
<span class="p">)</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">legend_orientation="h"</code>: 범례를 가로로 눕혀서 상단이나 하단에 배치할 때 유용하다.</li>
  <li><code class="language-plaintext highlighter-rouge">legend_x</code>, <code class="language-plaintext highlighter-rouge">legend_y</code>: 좌표값(0~1 정규화 좌표)을 이용해 아주 세밀한 위치 조정이 가능하다.</li>
</ul>

<hr />

<h2 id="3-템플릿template-적용-분위기-반전">3. 템플릿(Template) 적용: 분위기 반전</h2>

<p>Plotly는 잘 만들어진 내장 테마(Template)를 제공한다. <code class="language-plaintext highlighter-rouge">template</code> 인자 하나만 바꿔도 그래프의 느낌이 확 달라진다.</p>

<h3 id="31-ggplot2-스타일">3.1 ggplot2 스타일</h3>
<p>R의 시각화 라이브러리인 ggplot2 스타일이다. 회색 격자 배경이 특징이다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span> <span class="o">=</span> <span class="n">px</span><span class="p">.</span><span class="n">bar</span><span class="p">(</span>
    <span class="n">average_prices</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="s">'model'</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s">'price'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'transmission'</span><span class="p">,</span>
    <span class="n">title</span><span class="o">=</span><span class="s">'모델 변속기별 평균 가격_ggplot2'</span><span class="p">,</span>
    <span class="n">template</span><span class="o">=</span><span class="s">"ggplot2"</span>  <span class="c1"># 템플릿 적용
</span><span class="p">)</span>

<span class="c1"># 레이아웃 설정 (범례 우측 배치)
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_layout</span><span class="p">(</span>
    <span class="n">legend_orientation</span><span class="o">=</span><span class="s">"v"</span><span class="p">,</span>
    <span class="n">legend_x</span><span class="o">=</span><span class="mf">1.02</span>  <span class="c1"># 그래프 오른쪽 바깥
</span><span class="p">)</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="32-dark-모드">3.2 Dark 모드</h3>
<p>어두운 배경의 대시보드에 넣을 때 딱이다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span> <span class="o">=</span> <span class="n">px</span><span class="p">.</span><span class="n">bar</span><span class="p">(</span>
    <span class="n">average_prices</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="s">'model'</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s">'price'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'transmission'</span><span class="p">,</span>
    <span class="n">title</span><span class="o">=</span><span class="s">'모델 변속기별 평균 가격_dark'</span><span class="p">,</span>
    <span class="n">template</span><span class="o">=</span><span class="s">"plotly_dark"</span>  <span class="c1"># 다크 모드 적용
</span><span class="p">)</span>

<span class="c1"># 레이아웃 설정 (Navy 배경 범례)
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_layout</span><span class="p">(</span>
    <span class="n">legend_bgcolor</span><span class="o">=</span><span class="s">"Navy"</span><span class="p">,</span>
    <span class="n">legend_bordercolor</span><span class="o">=</span><span class="s">"DarkGray"</span><span class="p">,</span>
    <span class="n">legend_borderwidth</span><span class="o">=</span><span class="mi">2</span>
<span class="p">)</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<hr />

<h2 id="4-pandas-plotly-backend-생산성의-혁명">4. Pandas Plotly Backend: 생산성의 혁명</h2>

<p>이게 오늘 실습의 하이라이트다. Pandas의 기본 <code class="language-plaintext highlighter-rouge">.plot()</code>은 matplotlib 기반이라 인터랙티브하지 않다. 하지만 백엔드만 바꿔주면?</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 백엔드 설정: 이제부터 Pandas plot은 Plotly로 그려진다
</span><span class="n">pd</span><span class="p">.</span><span class="n">options</span><span class="p">.</span><span class="n">plotting</span><span class="p">.</span><span class="n">backend</span> <span class="o">=</span> <span class="s">'plotly'</span>

<span class="c1"># groupby 후 바로 plot() 호출
</span><span class="n">fig</span> <span class="o">=</span> <span class="n">bmw</span><span class="p">.</span><span class="n">groupby</span><span class="p">(</span><span class="s">'model'</span><span class="p">)[</span><span class="s">'price'</span><span class="p">].</span><span class="n">mean</span><span class="p">().</span><span class="n">plot</span><span class="p">(</span>
    <span class="n">kind</span><span class="o">=</span><span class="s">'bar'</span><span class="p">,</span> 
    <span class="n">title</span><span class="o">=</span><span class="s">'Pandas Backend로 그린 그래프'</span>
<span class="p">)</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">import plotly.express</code>를 안 해도 되고, <code class="language-plaintext highlighter-rouge">px.bar(df, ...)</code>처럼 인자를 따로 넘길 필요도 없다. 그냥 익숙한 Pandas 문법 뒤에 <code class="language-plaintext highlighter-rouge">.plot()</code>만 붙이면 줌, 팬, 툴팁이 되는 Plotly 그래프가 튀어나온다. EDA(탐색적 데이터 분석) 단계에서 엄청나게 편하다.</p>

<hr />

<h2 id="5-정리">5. 정리</h2>

<p>오늘은 시각화의 디테일과 편의성을 챙기는 팁들을 정리했다.</p>

<table>
  <thead>
    <tr>
      <th>기능</th>
      <th>속성/코드</th>
      <th>용도</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>범례 방향</td>
      <td><code class="language-plaintext highlighter-rouge">legend_orientation="h"</code></td>
      <td>가로 배치 (공간 효율)</td>
    </tr>
    <tr>
      <td>범례 위치</td>
      <td><code class="language-plaintext highlighter-rouge">legend_x</code>, <code class="language-plaintext highlighter-rouge">legend_y</code></td>
      <td>미세한 위치 조정</td>
    </tr>
    <tr>
      <td>범례 스타일</td>
      <td><code class="language-plaintext highlighter-rouge">legend_bgcolor/border</code></td>
      <td>배경색 및 테두리</td>
    </tr>
    <tr>
      <td>템플릿</td>
      <td><code class="language-plaintext highlighter-rouge">template="..."</code></td>
      <td><code class="language-plaintext highlighter-rouge">ggplot2</code>, <code class="language-plaintext highlighter-rouge">plotly_dark</code>, <code class="language-plaintext highlighter-rouge">seaborn</code> 등 테마 적용</td>
    </tr>
    <tr>
      <td>백엔드 설정</td>
      <td><code class="language-plaintext highlighter-rouge">pd.options.plotting.backend = 'plotly'</code></td>
      <td>Pandas 문법으로 Plotly 사용</td>
    </tr>
  </tbody>
</table>

<p>특히 <strong>Pandas Backend</strong> 설정은 실무에서 정말 유용하게 쓸 것 같다. 간단한 확인용 그래프는 이걸로, 보고서용 고퀄리티 그래프는 <code class="language-plaintext highlighter-rouge">graph_objects</code>로 그리는 전략으로 가야겠다.</p>

<hr />
<p><strong>Reference</strong></p>
<ul>
  <li>Data: BMW Used Car Sales(https://www.kaggle.com/datasets/ayeshaimran1619/bmw-sales-and-pricing-trends)</li>
</ul>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Python" /><category term="Plotly" /><category term="Pandas" /><category term="Visualization" /><summary type="html"><![CDATA[Plotly 마스터하기: 디테일과 편의성 잡기]]></summary></entry><entry><title type="html">[Python] Plotly Graph Objects로 데이터 시각화하기 (Part 2)</title><link href="https://kinkos1234.github.io/coding/Plotly_2/" rel="alternate" type="text/html" title="[Python] Plotly Graph Objects로 데이터 시각화하기 (Part 2)" /><published>2026-02-02T00:00:00+09:00</published><updated>2026-02-02T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/Plotly_2</id><content type="html" xml:base="https://kinkos1234.github.io/coding/Plotly_2/"><![CDATA[<h1 id="plotly-심화-축-디테일과-subplots-정복하기">Plotly 심화: 축 디테일과 Subplots 정복하기</h1>

<p>시각화의 퀄리티는 결국 ‘디테일’에서 결정된다고 생각한다. 오늘은 Plotly의 <strong>Graph Objects</strong>를 활용해서 축과 그리드를 내 마음대로 커스터마이징하고, <code class="language-plaintext highlighter-rouge">make_subplots</code>로 복잡한 레이아웃을 구성하는 방법을 정리해봤다.</p>

<p>실습 데이터로는 <strong>Kaggle Global Temperature Dataset</strong>을 사용했고, 서울, 도쿄, 베이징의 온도 변화를 시각화해봤다.</p>

<hr />

<h2 id="1-데이터-준비">1. 데이터 준비</h2>

<p>1849년부터 2013년까지의 전 세계 주요 도시 온도 데이터다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">plotly.graph_objects</span> <span class="k">as</span> <span class="n">go</span>
<span class="kn">from</span> <span class="nn">plotly.subplots</span> <span class="kn">import</span> <span class="n">make_subplots</span>

<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">'GlobalLandTemperatures_GlobalLandTemperaturesByMajorCity.csv'</span><span class="p">)</span>

<span class="c1"># 도시별 데이터 필터링 및 날짜 변환
</span><span class="k">def</span> <span class="nf">get_city_data</span><span class="p">(</span><span class="n">city_name</span><span class="p">):</span>
    <span class="n">temp</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">'City'</span><span class="p">]</span> <span class="o">==</span> <span class="n">city_name</span><span class="p">].</span><span class="n">copy</span><span class="p">()</span>
    <span class="n">temp</span><span class="p">[</span><span class="s">'dt'</span><span class="p">]</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">to_datetime</span><span class="p">(</span><span class="n">temp</span><span class="p">[</span><span class="s">'dt'</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">temp</span>

<span class="n">seoul_df</span> <span class="o">=</span> <span class="n">get_city_data</span><span class="p">(</span><span class="s">'Seoul'</span><span class="p">)</span>
<span class="n">tokyo_df</span> <span class="o">=</span> <span class="n">get_city_data</span><span class="p">(</span><span class="s">'Tokyo'</span><span class="p">)</span>
<span class="n">beijing_df</span> <span class="o">=</span> <span class="n">get_city_data</span><span class="p">(</span><span class="s">'Peking'</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="2-축과-그리드-커스터마이징-axis--grid">2. 축과 그리드 커스터마이징 (Axis &amp; Grid)</h2>

<p>기본 그래프는 깔끔하지만 어딘가 심심하다. 축 스타일을 명확하게 잡아서 ‘전문가스러운’ 느낌을 내보자.</p>

<h3 id="21-축선axis-line과-테두리mirror">2.1 축선(Axis Line)과 테두리(Mirror)</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>
<span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="n">seoul_df</span><span class="p">[</span><span class="s">'dt'</span><span class="p">],</span> <span class="n">y</span><span class="o">=</span><span class="n">seoul_df</span><span class="p">[</span><span class="s">'AverageTemperature'</span><span class="p">],</span> <span class="n">mode</span><span class="o">=</span><span class="s">'lines'</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">'Seoul'</span><span class="p">))</span>

<span class="c1"># X축, Y축 스타일 동시 설정
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_xaxes</span><span class="p">(</span><span class="n">showline</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">linecolor</span><span class="o">=</span><span class="s">'black'</span><span class="p">,</span> <span class="n">mirror</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">fig</span><span class="p">.</span><span class="n">update_yaxes</span><span class="p">(</span><span class="n">showline</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">linecolor</span><span class="o">=</span><span class="s">'black'</span><span class="p">,</span> <span class="n">mirror</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">mirror=True</code>: 반대편(오른쪽/위쪽)에도 축선을 그려서 그래프를 액자처럼 감싸준다. 훨씬 안정감 있어 보인다.</li>
</ul>

<h3 id="22-그리드grid-디테일-설정">2.2 그리드(Grid) 디테일 설정</h3>
<p>데이터 값을 더 정확하게 읽기 위해 보조 그리드(Minor Grid)까지 추가해봤다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 그리드 스타일 예시
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_xaxes</span><span class="p">(</span>
    <span class="n">showgrid</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">gridwidth</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">gridcolor</span><span class="o">=</span><span class="s">'lightgray'</span><span class="p">,</span> <span class="n">griddash</span><span class="o">=</span><span class="s">'dot'</span><span class="p">,</span>  <span class="c1"># 주요 그리드
</span>    <span class="n">minor_showgrid</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">minor_gridwidth</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">minor_gridcolor</span><span class="o">=</span><span class="s">'lightblue'</span><span class="p">,</span> <span class="n">minor_griddash</span><span class="o">=</span><span class="s">'dash'</span> <span class="c1"># 보조 그리드
</span><span class="p">)</span>
</code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">griddash='dot'</code>: 실선 대신 점선으로 그리드를 그려서 데이터에 집중할 수 있게 했다.</li>
</ul>

<hr />

<h2 id="3-subplots-그래프-나누고-합치기">3. Subplots: 그래프 나누고 합치기</h2>

<p>여러 그래프를 한 화면에 배치할 때는 <code class="language-plaintext highlighter-rouge">make_subplots</code>가 필수다.</p>

<h3 id="31-기본-subplots-1행-2열">3.1 기본 Subplots (1행 2열)</h3>
<p>서울과 도쿄의 온도를 나란히 비교해보자.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 1행 2열 구조 생성
</span><span class="n">fig</span> <span class="o">=</span> <span class="n">make_subplots</span><span class="p">(</span>
    <span class="n">rows</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">cols</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
    <span class="n">subplot_titles</span><span class="o">=</span><span class="p">(</span><span class="s">'Tokyo Temperature'</span><span class="p">,</span> <span class="s">'Seoul Temperature'</span><span class="p">),</span>
    <span class="n">shared_yaxes</span><span class="o">=</span><span class="bp">True</span>  <span class="c1"># Y축 공유 (비교 용이)
</span><span class="p">)</span>

<span class="c1"># add_trace시 row, col 지정 필수
</span><span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="n">tokyo_df</span><span class="p">[</span><span class="s">'dt'</span><span class="p">],</span> <span class="n">y</span><span class="o">=</span><span class="n">tokyo_df</span><span class="p">[</span><span class="s">'AverageTemperature'</span><span class="p">],</span> <span class="n">name</span><span class="o">=</span><span class="s">'Tokyo'</span><span class="p">),</span> <span class="n">row</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">col</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="n">seoul_df</span><span class="p">[</span><span class="s">'dt'</span><span class="p">],</span> <span class="n">y</span><span class="o">=</span><span class="n">seoul_df</span><span class="p">[</span><span class="s">'AverageTemperature'</span><span class="p">],</span> <span class="n">name</span><span class="o">=</span><span class="s">'Seoul'</span><span class="p">),</span> <span class="n">row</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">col</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="32-복잡한-레이아웃-specs--colspan">3.2 복잡한 레이아웃 (Specs &amp; Colspan)</h3>
<p>단순 격자가 아니라, 한 그래프가 두 칸을 차지하게 하려면 <code class="language-plaintext highlighter-rouge">specs</code> 옵션을 써야 한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 2행 2열 구조, 첫 번째 행은 통째로 쓰기(colspan=2)
</span><span class="n">fig</span> <span class="o">=</span> <span class="n">make_subplots</span><span class="p">(</span>
    <span class="n">rows</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">cols</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
    <span class="n">specs</span><span class="o">=</span><span class="p">[[{</span><span class="s">"colspan"</span><span class="p">:</span> <span class="mi">2</span><span class="p">},</span> <span class="bp">None</span><span class="p">],</span>  <span class="c1"># 1행: 2칸 병합
</span>           <span class="p">[{},</span> <span class="p">{}]],</span>               <span class="c1"># 2행: 2개 분리
</span>    <span class="n">subplot_titles</span><span class="o">=</span><span class="p">(</span><span class="s">'Seoul (Total)'</span><span class="p">,</span> <span class="s">'Tokyo'</span><span class="p">,</span> <span class="s">'Beijing'</span><span class="p">)</span>
<span class="p">)</span>

<span class="c1"># Trace 추가 생략 (위치만 맞춰주면 됨)
</span></code></pre></div></div>
<p>엑셀 셀 병합하듯이 <code class="language-plaintext highlighter-rouge">colspan</code>을 주면 되는데, 뒤에 <code class="language-plaintext highlighter-rouge">None</code>을 넣어줘야 하는 게 포인트다.</p>

<hr />

<h2 id="4-정리">4. 정리</h2>

<p>오늘 실습한 핵심 메서드들이다.</p>

<table>
  <thead>
    <tr>
      <th>기능</th>
      <th>메서드/옵션</th>
      <th>용도</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>축선 설정</td>
      <td><code class="language-plaintext highlighter-rouge">showline</code>, <code class="language-plaintext highlighter-rouge">linewidth</code></td>
      <td>축 선 표시 및 두께 설정</td>
    </tr>
    <tr>
      <td>테두리</td>
      <td><code class="language-plaintext highlighter-rouge">mirror=True</code></td>
      <td>그래프 반대편에도 축 표시</td>
    </tr>
    <tr>
      <td>그리드</td>
      <td><code class="language-plaintext highlighter-rouge">showgrid</code>, <code class="language-plaintext highlighter-rouge">minor_showgrid</code></td>
      <td>주요/보조 격자 표시</td>
    </tr>
    <tr>
      <td>분할 차트</td>
      <td><code class="language-plaintext highlighter-rouge">make_subplots()</code></td>
      <td>여러 그래프 레이아웃 생성</td>
    </tr>
    <tr>
      <td>축 공유</td>
      <td><code class="language-plaintext highlighter-rouge">shared_yaxes=True</code></td>
      <td>서브플롯 간 Y축 스케일 통일</td>
    </tr>
    <tr>
      <td>셀 병합</td>
      <td><code class="language-plaintext highlighter-rouge">specs=[{"colspan": 2}]</code></td>
      <td>서브플롯 공간 병합</td>
    </tr>
  </tbody>
</table>

<p>이 정도만 자유자재로 다뤄도 웬만한 시각화 요구사항은 다 맞출 수 있을 것 같다. 다음엔 상호작용(Interactive) 기능을 좀 더 파봐야겠다.</p>

<hr />

<p><strong>Reference</strong></p>
<ul>
  <li>Dataset: <a href="https://www.kaggle.com/datasets/maso0dahmed/global-temperature-records-1850-2022">Kaggle Global Temperature Records</a></li>
</ul>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Python" /><category term="Plotly" /><category term="Visualization" /><category term="Subplots" /><category term="Customization" /><summary type="html"><![CDATA[Plotly 심화: 축 디테일과 Subplots 정복하기]]></summary></entry><entry><title type="html">[Python] Plotly Graph Objects로 데이터 시각화하기 (Part 1)</title><link href="https://kinkos1234.github.io/coding/Plotly_1/" rel="alternate" type="text/html" title="[Python] Plotly Graph Objects로 데이터 시각화하기 (Part 1)" /><published>2026-01-31T00:00:00+09:00</published><updated>2026-01-31T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/Plotly_1</id><content type="html" xml:base="https://kinkos1234.github.io/coding/Plotly_1/"><![CDATA[<h1 id="plotly-graph-objects-실습-내-맘대로-그래프-뜯어고치기">Plotly Graph Objects 실습: 내 맘대로 그래프 뜯어고치기</h1>

<p>오늘은 Plotly의 <code class="language-plaintext highlighter-rouge">graph_objects</code>를 활용한 데이터 시각화 실습을 진행했다. 보통은 <code class="language-plaintext highlighter-rouge">plotly.express</code>가 편해서 많이 쓰지만, 디테일한 커스터마이징이 필요한 순간에는 결국 저수준 API인 <code class="language-plaintext highlighter-rouge">graph_objects</code>를 건드려야 한다.</p>

<p>이번 실습에서는 <strong>Kaggle Pokemon 데이터셋</strong>을 사용해서, 피규어 생성부터 축 조작까지 하나씩 뜯어보며 정리했다.</p>

<hr />

<h2 id="1-데이터-로드-및-준비">1. 데이터 로드 및 준비</h2>

<p>먼저 필요한 라이브러리와 데이터를 불러온다. 포켓몬 데이터는 HP, 공격(Attack), 방어(Defense) 등 수치형 데이터가 많아 시각화 연습하기 딱 좋다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">plotly.graph_objects</span> <span class="k">as</span> <span class="n">go</span>

<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">'Pokemon.csv'</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="2-기본-그래프-생성-figure--trace">2. 기본 그래프 생성 (Figure &amp; Trace)</h2>

<p><code class="language-plaintext highlighter-rouge">graph_objects</code>의 핵심은 <strong>빈 도화지(Figure)</strong>를 만들고, 그 위에 <strong>그림(Trace)</strong>을 하나씩 얹는 방식이다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span> <span class="o">=</span> <span class="n">go</span><span class="p">.</span><span class="n">Figure</span><span class="p">()</span>

<span class="c1"># 첫 번째 Trace: 공격력 vs 방어력
</span><span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span>
    <span class="n">x</span><span class="o">=</span><span class="n">df</span><span class="p">[</span><span class="s">'Attack'</span><span class="p">],</span>
    <span class="n">y</span><span class="o">=</span><span class="n">df</span><span class="p">[</span><span class="s">'Defense'</span><span class="p">],</span>
    <span class="n">mode</span><span class="o">=</span><span class="s">'markers'</span><span class="p">,</span>
    <span class="n">name</span><span class="o">=</span><span class="s">'Attack vs Defense'</span>
<span class="p">))</span>

<span class="c1"># 두 번째 Trace: 체력 vs 스피드
</span><span class="n">fig</span><span class="p">.</span><span class="n">add_trace</span><span class="p">(</span><span class="n">go</span><span class="p">.</span><span class="n">Scatter</span><span class="p">(</span>
    <span class="n">x</span><span class="o">=</span><span class="n">df</span><span class="p">[</span><span class="s">'HP'</span><span class="p">],</span>
    <span class="n">y</span><span class="o">=</span><span class="n">df</span><span class="p">[</span><span class="s">'Speed'</span><span class="p">],</span>
    <span class="n">mode</span><span class="o">=</span><span class="s">'markers'</span><span class="p">,</span>
    <span class="n">name</span><span class="o">=</span><span class="s">'HP vs Speed'</span>
<span class="p">))</span>

<span class="n">fig</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p>이렇게 하면 하나의 그래프 안에 두 종류의 상관관계가 동시에 표현된다. 범례(Legend)를 클릭해서 보고 싶은 데이터만 껐다 킬 수 있는 게 Plotly의 장점이다.</p>

<hr />

<h2 id="3-trace-업데이트-update-traces">3. Trace 업데이트 (Update Traces)</h2>

<p>이미 그려진 Trace들의 스타일을 일괄적으로, 혹은 선택적으로 바꾸고 싶을 때 <code class="language-plaintext highlighter-rouge">update_traces</code>를 쓴다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 'Attack vs Defense' Trace만 골라서 초록색으로 변경
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_traces</span><span class="p">(</span>
    <span class="n">marker</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">opacity</span><span class="o">=</span><span class="mf">0.6</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'green'</span><span class="p">),</span>
    <span class="n">selector</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">'Attack vs Defense'</span><span class="p">)</span>
<span class="p">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">selector</code> 옵션이 꿀기능이다. 이름을 지정해주면 해당 Trace만 콕 집어서 수정할 수 있다.</p>

<hr />

<h2 id="4-축axis-내-맘대로-조작하기">4. 축(Axis) 내 맘대로 조작하기</h2>

<p>그래프의 완성도는 축 설정에서 나온다. 범위, 제목, 그리드 등을 세밀하게 조정해보자.</p>

<h3 id="41-축-범위-및-제목-설정">4.1 축 범위 및 제목 설정</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># X축 설정
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_xaxes</span><span class="p">(</span>
    <span class="nb">range</span><span class="o">=</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">200</span><span class="p">],</span>                  <span class="c1"># 범위 고정
</span>    <span class="n">title_text</span><span class="o">=</span><span class="s">"Attack &amp; HP Stats"</span><span class="p">,</span>  <span class="c1"># 축 제목
</span>    <span class="n">showgrid</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>                   <span class="c1"># 그리드 표시
</span>    <span class="n">gridwidth</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
    <span class="n">gridcolor</span><span class="o">=</span><span class="s">'lightgray'</span>
<span class="p">)</span>
</code></pre></div></div>

<h3 id="42-축-숨기기">4.2 축 숨기기</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Y축 설정 (숨김 처리 예시)
</span><span class="n">fig</span><span class="p">.</span><span class="n">update_yaxes</span><span class="p">(</span>
    <span class="nb">range</span><span class="o">=</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">150</span><span class="p">],</span>
    <span class="n">title_text</span><span class="o">=</span><span class="s">"Defense &amp; Speed Stats"</span><span class="p">,</span>
    <span class="n">showgrid</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
    <span class="n">visible</span><span class="o">=</span><span class="bp">False</span>  <span class="c1"># 아예 축을 안 보이게 할 수도 있다
</span><span class="p">)</span>
</code></pre></div></div>

<p>특정 구간만 집중해서 보여주거나, 깔끔한 디자인을 위해 축을 숨겨야 할 때 유용하다.</p>

<hr />

<h2 id="5-정리">5. 정리</h2>

<p><code class="language-plaintext highlighter-rouge">plotly.express</code>가 ‘알아서 잘 딱 깔끔하게’ 그려준다면, <code class="language-plaintext highlighter-rouge">graph_objects</code>는 ‘내가 원하는 대로’ 그릴 수 있게 해준다.</p>

<table>
  <thead>
    <tr>
      <th>기능</th>
      <th>메서드</th>
      <th>용도</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>객체 생성</td>
      <td><code class="language-plaintext highlighter-rouge">go.Figure()</code></td>
      <td>빈 그래프 객체 생성</td>
    </tr>
    <tr>
      <td>데이터 추가</td>
      <td><code class="language-plaintext highlighter-rouge">add_trace()</code></td>
      <td>산점도, 선 등 데이터 레이어 추가</td>
    </tr>
    <tr>
      <td>스타일 변경</td>
      <td><code class="language-plaintext highlighter-rouge">update_traces()</code></td>
      <td>마커 크기, 색상 등 수정</td>
    </tr>
    <tr>
      <td>선택 수정</td>
      <td><code class="language-plaintext highlighter-rouge">selector</code></td>
      <td>특정 Trace만 골라서 수정</td>
    </tr>
    <tr>
      <td>축 설정</td>
      <td><code class="language-plaintext highlighter-rouge">update_xaxes/yaxes</code></td>
      <td>범위, 제목, 그리드 등 축 옵션 제어</td>
    </tr>
  </tbody>
</table>

<p>디테일에 익숙해지면 시각화가 훨씬 재밌어진다. 다음에는 더 복잡한 서브플롯(Subplots)도 다뤄봐야겠다.</p>

<hr />

<p><strong>Reference</strong></p>
<ul>
  <li>Dataset: <a href="https://www.kaggle.com/datasets/abcsds/pokemon">Kaggle Pokemon Dataset</a></li>
</ul>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Python" /><category term="Plotly" /><category term="Visualization" /><category term="DataAnalysis" /><summary type="html"><![CDATA[Plotly Graph Objects 실습: 내 맘대로 그래프 뜯어고치기]]></summary></entry><entry><title type="html">[Pandas] 월마트 판매 데이터 전처리 및 분석 기초</title><link href="https://kinkos1234.github.io/coding/Pandas_4/" rel="alternate" type="text/html" title="[Pandas] 월마트 판매 데이터 전처리 및 분석 기초" /><published>2026-01-29T00:00:00+09:00</published><updated>2026-01-29T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/Pandas_4</id><content type="html" xml:base="https://kinkos1234.github.io/coding/Pandas_4/"><![CDATA[<h1 id="pandas-실습-월마트-판매-데이터-전처리-part-4">Pandas 실습: 월마트 판매 데이터 전처리 (Part 4)</h1>

<p>지난 포스팅들에 이어, 이번에는 Kaggle의 <strong>Walmart Sales Dataset</strong>을 활용해 실전 전처리를 진행해봤다. 데이터 로드부터 필터링, 병합까지 데이터 분석의 가장 기초가 되는 과정을 정리해 본다.</p>

<hr />

<h2 id="1-데이터-로드-및-기초-탐색">1. 데이터 로드 및 기초 탐색</h2>

<p>분석의 시작은 언제나 데이터의 크기와 구성을 확인하는 것이다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>

<span class="c1"># 데이터셋 로드
</span><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">'Walmart_Sales.csv'</span><span class="p">)</span>

<span class="c1"># 데이터셋 크기 확인 (행, 컬럼)
</span><span class="k">print</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">shape</span><span class="p">)</span>
<span class="c1"># (6435, 8)
</span>
<span class="c1"># 데이터 정보 및 결측치 확인
</span><span class="k">print</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">info</span><span class="p">())</span>
</code></pre></div></div>

<p>확인 결과 총 6,435개의 행과 8개의 컬럼으로 구성되어 있다. 다행히 결측치(Null)는 없었고, 데이터 타입은 <code class="language-plaintext highlighter-rouge">float64</code>, <code class="language-plaintext highlighter-rouge">int64</code>, <code class="language-plaintext highlighter-rouge">object</code>(Date)로 섞여 있다.</p>

<hr />

<h2 id="2-피처-엔지니어링-feature-engineering">2. 피처 엔지니어링 (Feature Engineering)</h2>

<p>기존 데이터에서 필요한 정보를 뽑아내거나 불필요한 컬럼을 정리하는 과정이다.</p>

<h3 id="연도-추출">연도 추출</h3>
<p>날짜(Date) 컬럼에서 연도만 따로 보고 싶어서 슬라이싱을 통해 새로운 컬럼을 만들었다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Date 컬럼 예시: '12-02-2012'
# 뒤에서 4자리만 잘라서 'Year' 컬럼 생성
</span><span class="n">df</span><span class="p">[</span><span class="s">'Year'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'Date'</span><span class="p">].</span><span class="nb">str</span><span class="p">.</span><span class="nb">slice</span><span class="p">(</span><span class="o">-</span><span class="mi">4</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="컬럼-정리">컬럼 정리</h3>
<p>분석에 필요 없는 컬럼은 <code class="language-plaintext highlighter-rouge">pop()</code>을 사용해 제거했다. <code class="language-plaintext highlighter-rouge">pop()</code>은 컬럼을 삭제하면서 그 값을 반환해주니, 필요하다면 변수에 저장해 둘 수도 있다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 'Date' 컬럼 제거
</span><span class="n">dates</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">pop</span><span class="p">(</span><span class="s">'Date'</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="3-데이터-필터링-및-깊은-복사-deep-copy">3. 데이터 필터링 및 깊은 복사 (Deep Copy)</h2>

<p>원본 데이터를 건드리지 않고 안전하게 작업하기 위해 <strong>Deep Copy</strong>를 습관화해야 한다.</p>

<h3 id="deep-copy">Deep Copy</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 원본 데이터와 물리적으로 분리된 복사본 생성
</span><span class="n">df_copy</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">deep</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">deep=True</code>를 안 쓰면 껍데기만 복사돼서, 복사본을 수정했는데 원본이 같이 바뀌는 대참사가 일어날 수 있다.</p>

<h3 id="데이터-필터링">데이터 필터링</h3>
<p>공휴일이 아닌 날(평일)의 데이터만 보고 싶어서 필터링을 걸었다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Holiday_Flag가 0인(공휴일 아님) 데이터만 추출
</span><span class="n">non_holiday_df</span> <span class="o">=</span> <span class="n">df_copy</span><span class="p">[</span><span class="n">df_copy</span><span class="p">[</span><span class="s">'Holiday_Flag'</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">]</span>
</code></pre></div></div>

<hr />

<h2 id="4-데이터-분할-및-병합-concat">4. 데이터 분할 및 병합 (Concat)</h2>

<p>실무에서는 지점별로 엑셀 파일이 따로 오는 경우가 많다. 이를 시뮬레이션하기 위해 데이터를 쪼갰다가 다시 합쳐봤다.</p>

<h3 id="데이터-분리-segmentation">데이터 분리 (Segmentation)</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 지점(Store) 번호에 따라 데이터 분리
</span><span class="n">store1</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">'Store'</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">store2</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">'Store'</span><span class="p">]</span> <span class="o">==</span> <span class="mi">2</span><span class="p">]</span>
<span class="n">store3</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s">'Store'</span><span class="p">]</span> <span class="o">==</span> <span class="mi">3</span><span class="p">]</span>
</code></pre></div></div>

<h3 id="데이터-병합-concatenation">데이터 병합 (Concatenation)</h3>
<p><code class="language-plaintext highlighter-rouge">pd.concat()</code>을 쓰면 리스트에 담긴 데이터프레임들을 위아래로(수직) 붙여준다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 지점별 데이터 합치기
</span><span class="n">combined_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">concat</span><span class="p">([</span><span class="n">store1</span><span class="p">,</span> <span class="n">store2</span><span class="p">,</span> <span class="n">store3</span><span class="p">])</span>

<span class="c1"># 중복 데이터 제거 (혹시 모를 중복 방지)
</span><span class="n">combined_df</span> <span class="o">=</span> <span class="n">combined_df</span><span class="p">.</span><span class="n">drop_duplicates</span><span class="p">(</span><span class="n">subset</span><span class="o">=</span><span class="p">[</span><span class="s">'Store'</span><span class="p">,</span> <span class="s">'Date'</span><span class="p">])</span>
</code></pre></div></div>

<hr />

<h2 id="5-정리">5. 정리</h2>

<p>이번 실습에서 사용한 핵심 메서드들이다.</p>

<table>
  <thead>
    <tr>
      <th>기능</th>
      <th>메서드</th>
      <th>용도</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>데이터 로드</td>
      <td><code class="language-plaintext highlighter-rouge">pd.read_csv()</code></td>
      <td>CSV 파일 불러오기</td>
    </tr>
    <tr>
      <td>정보 확인</td>
      <td><code class="language-plaintext highlighter-rouge">df.info()</code>, <code class="language-plaintext highlighter-rouge">df.shape</code></td>
      <td>타입, 결측치, 크기 확인</td>
    </tr>
    <tr>
      <td>컬럼 추출/삭제</td>
      <td><code class="language-plaintext highlighter-rouge">df.pop()</code></td>
      <td>컬럼 삭제 및 반환</td>
    </tr>
    <tr>
      <td>깊은 복사</td>
      <td><code class="language-plaintext highlighter-rouge">df.copy(deep=True)</code></td>
      <td>원본 보존하며 복사</td>
    </tr>
    <tr>
      <td>데이터 결합</td>
      <td><code class="language-plaintext highlighter-rouge">pd.concat()</code></td>
      <td>여러 데이터프레임 수직 병합</td>
    </tr>
    <tr>
      <td>중복 제거</td>
      <td><code class="language-plaintext highlighter-rouge">drop_duplicates()</code></td>
      <td>중복된 행 제거</td>
    </tr>
  </tbody>
</table>

<p>직접 데이터를 만져보니 <code class="language-plaintext highlighter-rouge">copy(deep=True)</code>의 중요성을 다시 한번 느꼈다. 다음 포스팅에서는 시각화 라이브러리를 써서 이 데이터들을 그래프로 그려봐야겠다.</p>

<hr />

<p><strong>Reference</strong></p>
<ul>
  <li>Dataset: <a href="https://www.kaggle.com/datasets/mikhail1681/walmart-sales">Kaggle Walmart Sales Dataset</a></li>
</ul>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Pandas" /><category term="Python" /><category term="Preprocessing" /><category term="DataAnalysis" /><summary type="html"><![CDATA[Pandas 실습: 월마트 판매 데이터 전처리 (Part 4)]]></summary></entry><entry><title type="html">[Pandas] 정렬, 추출, 병합(Join/Merge) 정리</title><link href="https://kinkos1234.github.io/coding/Pandas_3/" rel="alternate" type="text/html" title="[Pandas] 정렬, 추출, 병합(Join/Merge) 정리" /><published>2026-01-28T00:00:00+09:00</published><updated>2026-01-28T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/Pandas_3</id><content type="html" xml:base="https://kinkos1234.github.io/coding/Pandas_3/"><![CDATA[<h1 id="pandas-정렬과-병합-월마트-주가-데이터로-실습">Pandas 정렬과 병합: 월마트 주가 데이터로 실습</h1>

<p>Pandas 학습으로 정렬과 병합을 정리했다. 이번에도 실제 데이터로 해봤는데, 1972년부터 2025년까지의 <strong>월마트(WMT) 주가 데이터</strong>를 사용했다.</p>

<hr />

<h2 id="1-정렬-sort_values-sort_index">1. 정렬: sort_values, sort_index</h2>

<p>데이터가 많을수록 원하는 순서로 정렬하는 게 분석의 시작이다.</p>

<h3 id="값-기준-정렬-sort_values">값 기준 정렬 (sort_values)</h3>

<p>특정 컬럼 값 기준으로 정렬할 때 쓴다. 거래량이 가장 많았던 날을 찾을 때:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 거래량 기준 내림차순 정렬
</span><span class="n">wmt_sorted</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">sort_values</span><span class="p">(</span><span class="n">by</span><span class="o">=</span><span class="s">'volume'</span><span class="p">,</span> <span class="n">ascending</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="인덱스-기준-정렬-sort_index">인덱스 기준 정렬 (sort_index)</h3>

<p>시계열 데이터가 뒤섞여 있을 때 날짜순으로 재정렬하는 데 쓴다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 날짜(Index) 기준 오름차순 정렬
</span><span class="n">wmt_timeline</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">sort_index</span><span class="p">(</span><span class="n">ascending</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="2-데이터-추출-nlargest-nsmallest">2. 데이터 추출: nlargest, nsmallest</h2>

<p>정렬 후 상위 몇 개 뽑을 때 <code class="language-plaintext highlighter-rouge">head()</code> 써도 되지만, <code class="language-plaintext highlighter-rouge">nlargest</code>가 더 직관적이다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 월마트 역대 최고가(Adj Close) TOP 5
</span><span class="n">top5_prices</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'adj_close'</span><span class="p">].</span><span class="n">nlargest</span><span class="p">(</span><span class="n">n</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>

<span class="c1"># 월마트 역대 최저가(Adj Close) BOTTOM 5
</span><span class="n">bottom5_prices</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'adj_close'</span><span class="p">].</span><span class="n">nsmallest</span><span class="p">(</span><span class="n">n</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
</code></pre></div></div>

<p>월마트 성장 추이를 빠르게 확인하는 데 유용하다.</p>

<hr />

<h2 id="3-함수-기반-결합-combine">3. 함수 기반 결합: combine</h2>

<p>단순 합치기가 아니라 “두 데이터 중 더 큰 값 선택” 같은 로직을 적용해서 합칠 때 쓴다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 두 시리즈 비교해서 더 큰 값 선택
</span><span class="k">def</span> <span class="nf">take_larger</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">s1</span> <span class="k">if</span> <span class="n">s1</span><span class="p">.</span><span class="nb">sum</span><span class="p">()</span> <span class="o">&gt;</span> <span class="n">s2</span><span class="p">.</span><span class="nb">sum</span><span class="p">()</span> <span class="k">else</span> <span class="n">s2</span>

<span class="c1"># 시가(Open)와 종가(Close) 중 더 높은 값으로 구성
</span><span class="n">combined_highs</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'open'</span><span class="p">].</span><span class="n">combine</span><span class="p">(</span><span class="n">df</span><span class="p">[</span><span class="s">'close'</span><span class="p">],</span> <span class="nb">max</span><span class="p">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">combine_first()</code>를 쓰면 첫 번째 데이터의 NaN을 두 번째 데이터 값으로 채울 수 있다.</p>

<hr />

<h2 id="4-인덱스-기반-병합-join">4. 인덱스 기반 병합: join</h2>

<p>서로 다른 데이터프레임을 <strong>인덱스 기준</strong>으로 옆으로 붙일 때 쓴다. 주가 데이터와 거래량 데이터를 따로 관리하다가 합칠 때 유용하다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 인덱스(Date)가 같은 두 데이터프레임 합치기
# how='inner', 'outer', 'left', 'right' 옵션으로 병합 방식 결정
</span><span class="n">full_data</span> <span class="o">=</span> <span class="n">price_df</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">volume_df</span><span class="p">,</span> <span class="n">how</span><span class="o">=</span><span class="s">'inner'</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="5-키key-기반-병합-merge">5. 키(Key) 기반 병합: merge</h2>

<p>SQL의 JOIN과 가장 유사하다. 공통된 <strong>컬럼</strong>을 기준으로 데이터를 병합한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 'Date' 열을 기준으로 주가 데이터와 외부 경제 지표 데이터 합치기
</span><span class="n">merged_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">merge</span><span class="p">(</span><span class="n">df</span><span class="p">,</span> <span class="n">economy_indices</span><span class="p">,</span> <span class="n">on</span><span class="o">=</span><span class="s">'Date'</span><span class="p">,</span> <span class="n">how</span><span class="o">=</span><span class="s">'left'</span><span class="p">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">merge</code>는 중복 키 처리나 다양한 병합 방식(<code class="language-plaintext highlighter-rouge">on</code>, <code class="language-plaintext highlighter-rouge">left_on</code>, <code class="language-plaintext highlighter-rouge">right_on</code>)을 지원해서 복잡한 데이터 분석에서 가장 많이 쓰게 된다.</p>

<hr />

<h2 id="정리">정리</h2>

<table>
  <thead>
    <tr>
      <th>기능</th>
      <th>메서드</th>
      <th>용도</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>값 기준 정렬</td>
      <td><code class="language-plaintext highlighter-rouge">sort_values()</code></td>
      <td>특정 컬럼 값으로 정렬</td>
    </tr>
    <tr>
      <td>인덱스 기준 정렬</td>
      <td><code class="language-plaintext highlighter-rouge">sort_index()</code></td>
      <td>인덱스(날짜 등)로 정렬</td>
    </tr>
    <tr>
      <td>상위/하위 추출</td>
      <td><code class="language-plaintext highlighter-rouge">nlargest()</code>, <code class="language-plaintext highlighter-rouge">nsmallest()</code></td>
      <td>TOP N, BOTTOM N 추출</td>
    </tr>
    <tr>
      <td>함수 기반 결합</td>
      <td><code class="language-plaintext highlighter-rouge">combine()</code></td>
      <td>커스텀 로직으로 합치기</td>
    </tr>
    <tr>
      <td>인덱스 기반 병합</td>
      <td><code class="language-plaintext highlighter-rouge">join()</code></td>
      <td>인덱스 기준으로 옆으로 붙이기</td>
    </tr>
    <tr>
      <td>키 기반 병합</td>
      <td><code class="language-plaintext highlighter-rouge">merge()</code></td>
      <td>공통 컬럼 기준으로 합치기 (SQL JOIN)</td>
    </tr>
  </tbody>
</table>

<p>이걸로 Pandas 기초 잡은 것 같다. 다양한 데이터를 만져보며 익숙해 지는게 관건일 듯.</p>

<hr />

<p><strong>데이터 출처</strong>: <a href="https://www.kaggle.com/datasets/umerhaddii/walmart-stock-data-2024">Kaggle - Walmart Stock Data 2025</a></p>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Pandas" /><category term="Python" /><category term="Merge" /><category term="Join" /><category term="데이터분석" /><summary type="html"><![CDATA[Pandas 정렬과 병합: 월마트 주가 데이터로 실습]]></summary></entry><entry><title type="html">[Pandas] Rolling, EWM으로 엔비디아 주가 이동평균선 구현</title><link href="https://kinkos1234.github.io/coding/Pandas_2/" rel="alternate" type="text/html" title="[Pandas] Rolling, EWM으로 엔비디아 주가 이동평균선 구현" /><published>2026-01-27T00:00:00+09:00</published><updated>2026-01-27T00:00:00+09:00</updated><id>https://kinkos1234.github.io/coding/Pandas_2</id><content type="html" xml:base="https://kinkos1234.github.io/coding/Pandas_2/"><![CDATA[<h1 id="rolling--ewm-실습-nvda-주가-데이터">Rolling &amp; EWM 실습: NVDA 주가 데이터</h1>

<p>지난번에 정리한 Pandas 기본 연산에 이어서, 이번엔 <code class="language-plaintext highlighter-rouge">rolling()</code>을 실제 데이터에 적용해봤다. 마침 Kaggle에 엔비디아(NVDA) 주가 데이터가 있길래 이걸로 이동평균선을 직접 만들어봤다.</p>

<hr />

<h2 id="1-데이터-로드">1. 데이터 로드</h2>

<p>Kaggle의 NVIDIA 일일 주가 데이터(<code class="language-plaintext highlighter-rouge">NVIDIA_STOCK.csv</code>)를 사용했다. 수정 종가(Adj Close) 기준으로 분석 진행.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>

<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">'NVIDIA_STOCK.csv'</span><span class="p">,</span> <span class="n">index_col</span><span class="o">=</span><span class="s">'Date'</span><span class="p">,</span> <span class="n">parse_dates</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">adj_close</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'Adj Close'</span><span class="p">]</span>
</code></pre></div></div>

<h2 id="2-이동평균선sma-구하기">2. 이동평균선(SMA) 구하기</h2>

<p>주가는 일간 변동이 심해서 그대로 보면 추세 파악이 어렵다. <code class="language-plaintext highlighter-rouge">rolling()</code>으로 특정 기간 평균을 구하면 노이즈가 줄어들어서 흐름이 보인다.</p>

<h3 id="5일--20일-이동평균">5일 / 20일 이동평균</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 5일 이동평균 (단기)
</span><span class="n">sma5</span> <span class="o">=</span> <span class="n">adj_close</span><span class="p">.</span><span class="n">rolling</span><span class="p">(</span><span class="n">window</span><span class="o">=</span><span class="mi">5</span><span class="p">).</span><span class="n">mean</span><span class="p">()</span>

<span class="c1"># 20일 이동평균 (중기)
</span><span class="n">sma20</span> <span class="o">=</span> <span class="n">adj_close</span><span class="p">.</span><span class="n">rolling</span><span class="p">(</span><span class="n">window</span><span class="o">=</span><span class="mi">20</span><span class="p">).</span><span class="n">mean</span><span class="p">()</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">window=20</code>이면 현재 행 포함 과거 20개 데이터의 평균을 계산한다.</p>

<hr />

<h2 id="3-파라미터-활용-min_periods-center">3. 파라미터 활용: min_periods, center</h2>

<p>실제로 돌려보니까 파라미터 설정에 따라 결과가 꽤 달라졌다.</p>

<h3 id="min_periods-초반-nan-문제-해결">min_periods: 초반 NaN 문제 해결</h3>

<p><code class="language-plaintext highlighter-rouge">window=20</code>이면 처음 19개 행은 데이터가 부족해서 NaN이 된다. <code class="language-plaintext highlighter-rouge">min_periods</code>를 설정하면 최소 몇 개부터 계산할지 정할 수 있다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 데이터 1개부터 계산 시작
</span><span class="n">sma20_filled</span> <span class="o">=</span> <span class="n">adj_close</span><span class="p">.</span><span class="n">rolling</span><span class="p">(</span><span class="n">window</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span> <span class="n">min_periods</span><span class="o">=</span><span class="mi">1</span><span class="p">).</span><span class="n">mean</span><span class="p">()</span>
</code></pre></div></div>

<p>초기 데이터 손실 없이 전체 구간을 채울 수 있어서 유용하다.</p>

<h3 id="center-후행성-줄이기">center: 후행성 줄이기</h3>

<p>기본값은 현재 시점 기준 과거 데이터만 보는데, <code class="language-plaintext highlighter-rouge">center=True</code>로 하면 현재를 중심으로 앞뒤 데이터를 같이 본다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 현재 시점 기준 전후 데이터 평균
</span><span class="n">sma20_centered</span> <span class="o">=</span> <span class="n">adj_close</span><span class="p">.</span><span class="n">rolling</span><span class="p">(</span><span class="n">window</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span> <span class="n">center</span><span class="o">=</span><span class="bp">True</span><span class="p">).</span><span class="n">mean</span><span class="p">()</span>
</code></pre></div></div>

<p>실제로 차트에 그려보니 <code class="language-plaintext highlighter-rouge">center=True</code> 쪽이 추세 전환점이 더 제때 잡히더라. 후행성(lagging) 문제가 좀 줄어든다.</p>

<hr />

<h2 id="4-지수가중이동평균ewm">4. 지수가중이동평균(EWM)</h2>

<p><code class="language-plaintext highlighter-rouge">rolling()</code>은 윈도우 내 모든 데이터를 동일하게 취급한다. 근데 주가 분석에서는 “최근 데이터가 더 중요하다”는 관점이 있다. 이럴 때 <code class="language-plaintext highlighter-rouge">ewm()</code>을 쓴다.</p>

<h3 id="ewm-기본-사용법">ewm 기본 사용법</h3>

<p>최근 데이터에 높은 가중치, 오래된 데이터는 가중치가 지수적으로 감소한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># span=20: 20일 기준 지수가중평균
</span><span class="n">ewm20</span> <span class="o">=</span> <span class="n">adj_close</span><span class="p">.</span><span class="n">ewm</span><span class="p">(</span><span class="n">span</span><span class="o">=</span><span class="mi">20</span><span class="p">).</span><span class="n">mean</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="rolling-vs-ewm">rolling vs ewm</h3>

<ul>
  <li><strong>반응 속도</strong>: <code class="language-plaintext highlighter-rouge">ewm</code>이 최근 변동에 더 민감하게 반응한다. 추세 전환이 빠를 때 유리하다.</li>
  <li><strong>NaN 처리</strong>: <code class="language-plaintext highlighter-rouge">rolling</code>은 윈도우가 다 차야 계산되는데, <code class="language-plaintext highlighter-rouge">ewm</code>은 데이터 1개부터 바로 계산 가능하다. <code class="language-plaintext highlighter-rouge">min_periods</code> 설정 없이도 초반 손실이 적다.</li>
</ul>

<p>실제로 그려보니 <code class="language-plaintext highlighter-rouge">rolling(20)</code>보다 <code class="language-plaintext highlighter-rouge">ewm(span=20)</code>이 최근 상승세를 더 밀착해서 따라간다.</p>

<hr />

<h2 id="정리">정리</h2>

<p>이동평균 계산하는 두 가지 방법을 정리하면:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">rolling()</code>: 윈도우 내 균등 가중. 안정적인 추세 확인용.</li>
  <li><code class="language-plaintext highlighter-rouge">ewm()</code>: 최근 데이터 가중. 변화에 민감하게 반응해야 할 때.</li>
</ul>

<p>실제 주가 데이터에 둘 다 적용해보니까 차이가 확실히 느껴진다. 상황에 따라 골라 쓰면 될 듯.</p>

<p>다음엔 정렬(sort) 관련 내용 정리할 예정.</p>

<hr />

<p><strong>데이터 출처</strong>: <a href="https://www.kaggle.com/datasets/muhammaddawood42/nvidia-stock-data">Kaggle - NVIDIA Stock Data</a></p>]]></content><author><name>Comad.J</name></author><category term="coding" /><category term="Pandas" /><category term="Python" /><category term="Rolling" /><category term="EWM" /><category term="주가분석" /><summary type="html"><![CDATA[Rolling &amp; EWM 실습: NVDA 주가 데이터]]></summary></entry></feed>