<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>북극곰의 개발일지</title>
    <link>https://cow-coding.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 7 Jun 2026 19:28:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>DevPolar</managingEditor>
    <image>
      <title>북극곰의 개발일지</title>
      <url>https://tistory1.daumcdn.net/tistory/3748269/attach/7820a37c55ce41c0a9e8c2aabeb91e2e</url>
      <link>https://cow-coding.tistory.com</link>
    </image>
    <item>
      <title>[파이썬 깊게 파보기] 3. Tuple / Dictionary / Set</title>
      <link>https://cow-coding.tistory.com/72</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;튜플의 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 리스트만큼 많이 사용되는 자료구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 데이터를 묶음으로 반환하는 많은 함수나 메소드들은 리턴값을 튜플로 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 튜플에 대한 이해는 리스트 못지 않게 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같이 보면 좋은 저장소는 &lt;a href=&quot;https://github.com/python/cpython/blob/main/Objects/tupleobject.c&quot;&gt;CPython : Tuple Object&lt;/a&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 이번 글은 사진보다 글 위주로 될 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;튜플도 객체들의 모음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 리스트는 객체 참조자들을 저장하는 컨테이너라고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 요소를 순서대로 결합한 요소들의 쌍이다. 리스트와 마찬가지로 여러 자료형들을 함께 저장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 점에서 왜 굳이 리스트와 구분되는 자료형이 있는걸까?&lt;br /&gt;면접에서도 자주 나오는 질문인데, &lt;b&gt;파이썬에서 리스트와 튜플의 차이점은 무엇일까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 자료구조의 가장 큰 차이점은 &lt;b&gt;값을 수정할 수 있는 가변객체냐, 불변객체냐&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 왜 굳이 이게 중요하냐라는 생각이 들 수 있는데, 데이터를 변화시키면 안되는 경우가 존재할 수 있기 때문이다. 파이썬에서 함수의 리턴값을 여러 개로 하면 팩킹의 형태로 변수가 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 함수가 리턴하면서 혹시 모르게 값이 변화되는 것을 막아줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에는 리스트와 큰 차이는 없다. 객체의 참조들을 저장하는 특징도 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 가변객체냐 불변객체냐는 파이썬의 메모리관리 측면에서 중요한 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 결과적으로 속도와도 연관이 되는데, 다시 &lt;a href=&quot;https://cow-coding.tistory.com/70&quot;&gt;맨 처음 포스트&lt;/a&gt;로 돌아가보면 -5 ~ 256은 미리 만들어진 객체를 사용한다고 했다. 그래서 해당 범위의 수를 사용하는 경우에는 약간이나마 조금 더 시간이 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플의 특징을 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트보다 적은 메모리를 차지하고 성능면에서 더 빠르다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트보다 적은 메모리를 차지하는 이유는 &lt;a href=&quot;https://cow-coding.tistory.com/71&quot;&gt;이전에&lt;/a&gt; 말했듯이 리스트는 확장에 대비하고자 좀 더 여유있게 메모리를 확보한다. 하지만 튜플은 내부 구조를 변화하지 않으므로 굳이 메모리를 여유있게 확보할 필요가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;내부 값을 실수로 변경하는 것에 대비할 수 있다.&lt;/li&gt;
&lt;li&gt;딕셔너리 키로 사용가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;튜플과 변수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플과 리스트를 생성하는 방식에는 각자의 이름을 가진 함수를 호출하는 방식이 있다. 이 둘은 생김새는 동일하지만 동작과정에서 큰 차이가 있다. &lt;b&gt;이는 결국 가변 객체냐 불변 객체냐로 다시 연결된다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;lst1 = [1, 2, 3, 4]
lst2 = list(lst1)

tp1 = (1, 2, 3, 4)
tp2 = tuple(tp1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;lst1&lt;/code&gt;과 &lt;code&gt;lst2&lt;/code&gt;는 서로 다른 객체가 생성되어 각각의 변수가 참조한다고 했다. 그렇다면 튜플은 어떻게 될까? 아주 잠깐 생각해보면 쉽게 답을 유추할 수 있다. 힌트는 불변 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 튜플의 아이덴티티는 동일하게 나온다. 왜일까? 이는 튜플의 생성 방식때문에 나타난다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;tp1.png&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbnSb4/btsugkuYbMM/JCwMZWwXtBNum0cNjZ3cd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbnSb4/btsugkuYbMM/JCwMZWwXtBNum0cNjZ3cd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbnSb4/btsugkuYbMM/JCwMZWwXtBNum0cNjZ3cd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbnSb4%2FbtsugkuYbMM%2FJCwMZWwXtBNum0cNjZ3cd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;82&quot; data-filename=&quot;tp1.png&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 동일한 튜플 객체에 대해 같은 참조를 하게된다. 이는 튜플이 불변 객체라 어차피 변경을 할 수 없는 데이터라서다. 그래서 같은 실체를 함께 참조하더라도 큰 문제가 발생하지 않기 때문에 동일한 참조를 가리킨다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;튜플의 누적 대입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 불변 객체라서 누적 대입(&lt;code&gt;+=&lt;/code&gt;)이 불가능할 것 같지만 사용할 수 있다. 하지만 리스트의 누적 대입의 메모리 활용과 다르게 동작한다. 이유는 역시나 불변 객체라서이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;tp2.png&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chgKK8/btsueh6AgZo/7XcsXhMR3J9348KJqF6Cz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chgKK8/btsueh6AgZo/7XcsXhMR3J9348KJqF6Cz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chgKK8/btsueh6AgZo/7XcsXhMR3J9348KJqF6Cz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchgKK8%2Fbtsueh6AgZo%2F7XcsXhMR3J9348KJqF6Cz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;508&quot; height=&quot;112&quot; data-filename=&quot;tp2.png&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;tp1 = (1, 2, 3, 4)
tp1 += (5, )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누적 대입을 하게되면 새로운 튜플이 만들어지고 객체 참조가 바뀌게 된다. 이 점을 잘 알아둘 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 정리하면 튜플은 동일한 객체는 새로 생성하지 않고 참조를 할당하고 조금이라도 변화를 주면 새로운 객체를 만들어서 참조한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리의 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리도 튜플과 리스트 못지 않게 많이 사용되는 자료구조이다. Key-Value의 쌍으로 이루어진 자료구조이고 자료구조 수업시간에 배우는 딕셔너리 구조를 생각하면된다. 여기서 조금 센스있는 사람 또는 열심히 자료구조 공부를 한 사람이라면 스쳐지나가는 생각이 있다. &lt;b&gt;딕셔너리는 해시테이블 구조를 가질까?&lt;/b&gt;&lt;br /&gt;정답은 &lt;b&gt;맞다&lt;/b&gt;이다. 딕셔너리는 해시 테이블을 통해 구현되었기 때문에 키 탐색이 빠르다는 장점이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;딕셔너리의 키&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 Key-Value가 한 쌍으로 이루어진 자료구조이다. 대부분의 언어에서 Key-Value 쌍의 자료구조에서 Key가 중복되지 않는 고유한 값이어야한다. 이는 파이썬도 마찬가지이다.&lt;br /&gt;우선 딕셔너리는 가변 객체에 속한다. 하지만 딕셔너리의 key는 불변이다. 일반적으로 키에 문자열을 많이 쓰지만 불변 객체라면 모두 사용할 수 있다. 이 말은 &lt;b&gt;튜플도 키로 쓸 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;딕셔너리에서 값을 가져올때는 &lt;code&gt;get&lt;/code&gt;을 써라&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 읽는 대부분의 사람들이 기본적인 딕셔너리 생성이나 사용법은 알 것이라 생각한다. 딕셔너리에서 value를 가져오는 방법은 크게 2가지가 존재한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dict[key]&lt;/li&gt;
&lt;li&gt;dict.get(key)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 중 어떤걸 쓰든 값이 '있다면', 가져오는 데엔 큰 특징은 없다. 하지만 &lt;code&gt;dict.get(key)&lt;/code&gt;로 값을 가져오는 것을 추천한다. 이유는 인덱스식으로 값을 가져올 경우 해당하는 key가 없다면 &lt;code&gt;KeyError&lt;/code&gt; 예외를 발생시킨다. 하지만 &lt;code&gt;dict.get(key)&lt;/code&gt;은 자신이 원한 값은 default로 설정해서 key가 없는 경우 default값을 반환한다. default를 따로 설정하지 않으면 None이 기본으로 설정되어 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;딕셔너리 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에서 &lt;code&gt;update&lt;/code&gt;를 사용하면 딕셔러니를 합치거나 새로운 쌍을 추가할 수 있다. 활용은 아래처럼 사용한다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;rgb = {&quot;red&quot;: &quot;빨강&quot;, &quot;blue&quot;: &quot;파랑&quot;, &quot;green&quot;: &quot;초록&quot;}
fruit = {&quot;yellow&quot;: &quot;바나나&quot;, &quot;red&quot;: &quot;사과&quot;, &quot;purple&quot;: &quot;포도&quot;}

rgb.update(fruit)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 만약 위의 예제처럼 같은 키인데 다른 value를 가지면 어떻게 될까?&lt;br /&gt;update라는 이름에 걸맞게 새롭게 들어오는 key-value 기준에 맞춰 데이터가 바뀌게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;update&lt;/code&gt;를 활용하면 특정 키의 요소를 변경하는 것도 가능하다. 이때, &lt;code&gt;dict.update(key=value)&lt;/code&gt;를 적으면 되는데 오타가 아니라 key는 문자열로 적지 않고 key 이름 자체를 적는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;set의 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set은 이름 그대로 집합이다. 중학교에 들어가서 수학책을 피면 가장 맨 처음 만나는 친구가 집합이다. 아마 공부를 아무리 안 한 사람이라도 집합은 공부했을 것이다. 왜냐? 첫 챕터니까...&lt;br /&gt;집합의 가장 큰 특징은 순서와 중복이 없는 값들의 모임이다. 이런 수학적 성질은 파이썬의 set에서도 동일하게 적용된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;set의 선언&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set을 선언하는 방식은 여러가지가 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;st1 = {1}
st2 = set()
st3 = set([1, 2, 3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의해야할 것은 빈 set을 만들때는 반드시 &lt;code&gt;set()&lt;/code&gt;을 사용해야한다. 이유는 set을 나타내는 연산자는 &lt;code&gt;{}&lt;/code&gt;인데, 만약 빈 &lt;code&gt;{}&lt;/code&gt;을 사용할 경우 빈 딕셔너리가 만들어진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;set에 대한 이해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set은 가변 객체이다. 물론 불변 객체로 사용하는 방법도 있다. 이는 fronzeset 타입이 있고 필요에 따라 라이브러리를 활용해 쓸 수 있다. 물론 불변 객체니까 frozenset은 딕셔너리 키로 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set도 값을 추가하거나 삭제할 수 있다. 추가를 할 때는 &lt;code&gt;set.add(e)&lt;/code&gt;를 사용하면 값이 추가된다. 물론 이미 있으면 추가되지 않는다.&lt;br /&gt;제거하는 과정에는 2개의 메소드가 있다. 하나는 &lt;code&gt;discard&lt;/code&gt;, 다른 하나는 &lt;code&gt;remove&lt;/code&gt;이다. 둘 중 어느 것이 좋냐?라는 질문을 한다면, 에러를 발생시키지 않는 쪽이 서비스하기에 유리할 것이다. 그렇다면 &lt;code&gt;discard&lt;/code&gt;를 써야한다. &lt;code&gt;remove&lt;/code&gt;는 값이 없다면 &lt;code&gt;KeyError&lt;/code&gt; 예외를 발생시킨다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬을 깊게 다뤄보는 3번째 시간이었다. 자료형을 그냥 쓰면 되는거지...라는 생각이 들 수도 있는데, 파이썬을 익숙하게 다룬다고 말하거나 다뤄야 한다면 변수가 어떻게 동작하는지, 메모리 구조를 어떻게 처리하는지는 알아야하지 않을까 싶다.&lt;/p&gt;</description>
      <category>언어 깊게 파보기/Python</category>
      <category>Python</category>
      <category>set</category>
      <category>딕셔너리</category>
      <category>집합</category>
      <category>튜플</category>
      <category>파이썬</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/72</guid>
      <comments>https://cow-coding.tistory.com/72#entry72comment</comments>
      <pubDate>Sun, 17 Sep 2023 23:51:04 +0900</pubDate>
    </item>
    <item>
      <title>[파이썬 깊게 파보기] 2. list</title>
      <link>https://cow-coding.tistory.com/71</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트의 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 파이썬을 만나게 된 이후 가장 많이 사용하는 자료구조라고 생각한다. 파이썬에서는 C++이나 Java와 같은 배열 자료구조를 제공하지 않고 그 역할을 리스트가 대신한다. 물론 리스트라는 자료구조는 단순히 배열에 대입해서 보기에는 상당히 복잡한 형태로 구성된 자료구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같이 보면 좋은 저장소는 &lt;a href=&quot;https://github.com/python/cpython/blob/main/Objects/listobject.c&quot;&gt;CPython : List Object&lt;/a&gt;이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리스트는 객체 참조자들의 저장소&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/image/deep/python/list.png&quot; alt=&quot;&quot; /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;list.png&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6WFbN/btsuenZ2JsZ/dubPXH0HBAWtMkeOyUB2Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6WFbN/btsuenZ2JsZ/dubPXH0HBAWtMkeOyUB2Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6WFbN/btsuenZ2JsZ/dubPXH0HBAWtMkeOyUB2Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6WFbN%2FbtsuenZ2JsZ%2FdubPXH0HBAWtMkeOyUB2Q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;262&quot; data-filename=&quot;list.png&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트의 활용성이 높은 것은 &lt;b&gt;다양한 자료형 객체의 저장 컨테이너&lt;/b&gt;라는 것과 &lt;b&gt;유동적인 데이터 변경&lt;/b&gt;이라는 특징때문이다. 여기서 중요한 것은 객체의 저장 컨테이너라는 점이다. 리스트에 저장되는 모든 요소는 &lt;b&gt;객체를 참조하는&lt;/b&gt; 역할을 하기 때문에 컨테이너라고 부른다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리스트와 변수&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;list2.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qWzIB/btst83ON3wZ/dhP7hAGovnKcSHKxDO8g7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qWzIB/btst83ON3wZ/dhP7hAGovnKcSHKxDO8g7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qWzIB/btst83ON3wZ/dhP7hAGovnKcSHKxDO8g7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqWzIB%2Fbtst83ON3wZ%2FdhP7hAGovnKcSHKxDO8g7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;142&quot; data-filename=&quot;list2.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;lst1 = [1, 2, 3, 4, 5]
lst2 = lst1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 다뤄본 변수에 대한 대입연산은 리스트도 동일하게 이뤄진다. 그렇기 때문에 리스트를 다른 리스트에 대입하는 연산을 수행하는 경우 동일 리스트를 복사하는 것이 아닌 &lt;b&gt;서로 같은 리스트를 포인팅&lt;/b&gt;하는 것이 된다. 따라서 다른 값을 변경해도 마치 C++의 포인터를 통한 변경처럼 다른 변수에도 영향을 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;list3.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEIxUv/btst7uTsDVz/1k85jlJngI8Ceekud9TiHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEIxUv/btst7uTsDVz/1k85jlJngI8Ceekud9TiHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEIxUv/btst7uTsDVz/1k85jlJngI8Ceekud9TiHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEIxUv%2Fbtst7uTsDVz%2F1k85jlJngI8Ceekud9TiHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;282&quot; data-filename=&quot;list3.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;lst1 = [1, 2, 3, 4, 5]
lst2 = [1, 2, 3, 4, 5]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 만약 동일한 리스트 코드를 &lt;b&gt;각각 생성하면&lt;/b&gt; 다른 객체를 참조한다. 여기까지 다룬 내용들에서도 아주 중요한 것들이 많다. 키워드 문장들을 고르면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트의 각 요소는 파이썬 객체들을 참조한다.&lt;/li&gt;
&lt;li&gt;리스트는 대입연산을 사용하면 동일 객체를 포인팅한다.&lt;/li&gt;
&lt;li&gt;리스트는 각각 생성하면 다른 객체를 참조한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 3가지 핵심 특성은 뒤에서 설명할 리스트 복사의 개념에서 아주 중요한 것들이다. 일단 이렇게 알아두자.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트의 내부 구조&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리스트의 내부&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 자료구조에서 &lt;b&gt;연결 리스트 (Linked List)&lt;/b&gt; 를 배워본 기억이 있을 것이다. 보통 링크드 리스트와 함께 비교하는 자료구조는 배열이 있다. 배열의 장점은 여러가지가 있지만 대표적인 것은 &lt;b&gt;Random Access가 가능하다는 것&lt;/b&gt;과 &lt;b&gt;연속적인 메모리 할당으로 빠른 접근 속도&lt;/b&gt;이다. 하지만 그 단점인 유동적인 크기의 변경이 불가능하다는 문제가 있다. 그래서 배열의 장점을 일부 포기하고 확장성을 갖는 자료구조가 바로 링크드 리스트이다. 링크드 리스트의 문제점 중 대표적인 것은 &lt;b&gt;불연속적인 메모리 할당&lt;/b&gt;이라는 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬의 리스트의 이름을 처음 들으면 링크드 리스트 구조라고 생각할 수 있다. 실제로 리스트의 특징을 갖고 있으며 배열의 특징도 갖고 있어서 단순히 Random Access를 지원하는 링크드 리스트라 생각할 수 있다. 하지만 파이썬의 리스트는 &lt;b&gt;연속된 메모리에 요소를 저장하는 배열로 동작&lt;/b&gt;한다. 그리고 리스트는 확장에 대비해서 여유있게 메모리를 미리 확보하는 특징이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대입과 누적 대입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트를 확장하는 방법으로는 &lt;code&gt;append&lt;/code&gt;, &lt;code&gt;extend&lt;/code&gt;, &lt;code&gt;x = x + [1]&lt;/code&gt; , &lt;code&gt;x += [1]&lt;/code&gt; 과 같은 방식이 있다. &lt;code&gt;append&lt;/code&gt;와 &lt;code&gt;extend&lt;/code&gt;는 기본적으로 기존 리스트의 참조를 확장하는 방식이다. 하지만 누적대입(&lt;code&gt;+=&lt;/code&gt;)과 대입(&lt;code&gt;=&lt;/code&gt;)은 파이썬 내부 동작이 다르게 동작한다. 이 점을 고려하면 좀 더 빠른 연산을 고려할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;list4.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IA1gs/btsuqBQpNPK/O1kklQfX8PIg0Ic6QKN2wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IA1gs/btsuqBQpNPK/O1kklQfX8PIg0Ic6QKN2wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IA1gs/btsuqBQpNPK/O1kklQfX8PIg0Ic6QKN2wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIA1gs%2FbtsuqBQpNPK%2FO1kklQfX8PIg0Ic6QKN2wk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;282&quot; data-filename=&quot;list4.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;x = [1, 2, 3, 4]
x = x + [5, 6]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 대입을 사용하는 리스트 확장은 기존의 파이썬의 대입의 특징을 갖는다. 즉, 새롭게 객체를 만들고 변수는 새로운 객체를 참조한다. 결국 &lt;b&gt;새로운 객체를 생성&lt;/b&gt;하는 추가적인 시간이 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;list5.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GxnVI/btsugTjMr7Q/QVLAhhfRUwqmEe7EWkUQL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GxnVI/btsugTjMr7Q/QVLAhhfRUwqmEe7EWkUQL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GxnVI/btsugTjMr7Q/QVLAhhfRUwqmEe7EWkUQL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGxnVI%2FbtsugTjMr7Q%2FQVLAhhfRUwqmEe7EWkUQL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;132&quot; data-filename=&quot;list5.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;x = [1, 2, 3, 4]
x += [5, 6]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 누적대입(&lt;code&gt;+=&lt;/code&gt;)은 기존의 리스트에 확장이 되는 방식으로 연산한다. 따라서 기존 리스트 객체가 변화하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트 복사&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트를 사용하다보면 가장 많이 마주치는 문제가 바로 리스트 복사 이슈이다. 특히 2차원 배열 형태의 리스트를 사용하면 단순 &lt;code&gt;list.copy()&lt;/code&gt;를 사용하는 것에 문제가 발생하기도 한다. 그래서 좀 더 자세히 리스트 복사와 관련된 내용을 알아보고, 각 방법에 따른 리스트의 구조 변화를 알아보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리스트의 단순 복사&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a = [1, 2, 3, 4]
b = a

b[0] = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 전 포스팅에서 다뤘던 예제이다. 이 경우 이제는 익숙하겠지만 b는 a 리스트 객체를 참조하기 때문에 a에도 영향을 준다. 리스트의 원본을 유지하면서 변화하는 조작은 프로그래밍에서 상당히 많이 사용되는 기법이다. 보통 변화를 주고 조건 확인 후 문제가 발생하면 원본으로 복구하고, 문제가 없다면 원본에 덮어씌우는 방식에 많이 쓴다. 이런 경우 파이썬은 문제가 될 수 있는 부분들이 있다. 문제들을 해결하고자 파이썬은 &lt;code&gt;copy&lt;/code&gt;와 같은 메소드를 쓴다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a = [1, 2, 3, 4]
b = a.copy()

b[0] = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 간단한 방법은 &lt;code&gt;list.copy()&lt;/code&gt;를 사용해서 리스트 복사본을 생성하는 것이다. 이렇게 되면 리스트의 동일한 객체를 복사본을 새롭게 생성하여 리턴한다. 즉 새로운 id를 부여받은 객체를 참조한다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;a = [1, 2, 3, 4]
b = list(a)

b[0] = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 방법은 &lt;code&gt;list&lt;/code&gt; 함수를 사용해서 &lt;b&gt;새로운 리스트를 생성&lt;/b&gt;하여 반환하는 것이다. 잠깐 &lt;a href=&quot;#%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%99%80-%EB%B3%80%EC%88%98&quot;&gt;위로&lt;/a&gt; 올라가보면...&lt;br /&gt;리스트의 주요 특징 중 각각 생성한 리스트는 새로운 객체들을 참조한다는 것이 있었다. 결국 이 방식은 완전히 새로운 리스트를 생성하는 것과 동일하기 때문에 복사와 같은 동작을 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a = [1, 2, 3, 4]
b = a[:]

b[0] = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로는 리스트 슬라이스를 모든 인덱스로 하면 새로운 리스트가 생성된다. 두 리스트에 대해 객체 비교를 하면 서로 다르게 나타난다. (하지만 튜플은 같다고 나타난다. 이는 이후 설명할 튜플과 딕셔너리에서 다루겠다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;얕은 복사, 깊은 복사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 복사를 알았으니 자유롭게 리스트를 변경할 수 있다고 생각하겠지만... 절반만 맞는 말이다. 파이썬의 변수가 객체를 참조한다는 점에서 참 골치가 아픈 일이 발생한다. 아래 코드의 출력 결과는 과연 뭘까?&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;a = [[1, 2], [3, 4]]
b = list(a)

b[0][1] = 1

print(a)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말한 대로라면 &lt;code&gt;[[1, 2], [3, 4]]&lt;/code&gt;가 나와야 하지만, &lt;code&gt;[[1, 1], [3, 4]]&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 이렇게 되는 이유는 &lt;b&gt;파이썬의 변수는 객체를 참조한다&lt;/b&gt;는 것과 &lt;b&gt;리스트는 객체 참조자들의 컨테이너&lt;/b&gt;라는 점때문에 발생한다. 위에서 말한 리스트 복사는 변수가 리스트를 참조만 하는 것을 막기 위해 &lt;b&gt;동일한 값을 참조하는 참조자들을 저장하는 리스트를 새롭게 만드는 것이다.&lt;/b&gt; 이게 참 머리가 아파지는 부분이다. 이해를 돕고자 그림으로 어떻게 된 상태인지 보여주겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;copy1.png&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxmdaD/btsuqzZpg2E/tkSaFCD1Brsdc4YdXNKNY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxmdaD/btsuqzZpg2E/tkSaFCD1Brsdc4YdXNKNY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxmdaD/btsuqzZpg2E/tkSaFCD1Brsdc4YdXNKNY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxmdaD%2FbtsuqzZpg2E%2FtkSaFCD1Brsdc4YdXNKNY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;731&quot; height=&quot;322&quot; data-filename=&quot;copy1.png&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 말하면 우리가 복사하는 리스트는 &lt;code&gt;PyListObject&lt;/code&gt; 객체 자체를 복사한다. 결국 참조자들은 모두 동일하게 유지된다. 그래서 여기서는 매우 특이한 특징이 나타나는데, 앞서 다룬 포스팅에서 -5 ~ 256까지는 동일 숫자 객체를 무조건 지시하지만 범위 밖의 숫자 객체를 매번 새롭게 생성된다고 했다. &lt;b&gt;하지만 만약 리스트 안에 있는 257은 리스트를 복제하면 다른 객체가 될까?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;copy2.png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rQ464/btsuAq11QAz/KGKkVIYyNG28zoh13Ked20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rQ464/btsuAq11QAz/KGKkVIYyNG28zoh13Ked20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rQ464/btsuAq11QAz/KGKkVIYyNG28zoh13Ked20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrQ464%2FbtsuAq11QAz%2FKGKkVIYyNG28zoh13Ked20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;820&quot; height=&quot;224&quot; data-filename=&quot;copy2.png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답을 말하면 &lt;b&gt;&quot;아니다&quot;&lt;/b&gt; 이다. 이유는 앞서 말한 이유인데, 결국 참조자를 복사하는 것이고 참조자들은 동일 객체를 가리키고 있다. 결국 이런 이유 때문에 앞에 나온 2차원 배열의 문제가 왜 그렇게 나오는지 알 수 있다. 2차원 배열의 구조를 디테일하게 나타내면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;copy3.png&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5XMds/btsubKnXDZo/ucwBpCgnjPLtBKBZudru9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5XMds/btsubKnXDZo/ucwBpCgnjPLtBKBZudru9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5XMds/btsubKnXDZo/ucwBpCgnjPLtBKBZudru9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5XMds%2FbtsubKnXDZo%2FucwBpCgnjPLtBKBZudru9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;451&quot; data-filename=&quot;copy3.png&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 바깥 리스트의 참조자를 복사하는 것은 성공했지만 그 참조자가 가리키는 내부의 리스트는 복사하지 못했기 때문에 발생하는 문제였다. 이 문제를 해결하려면 내부의 리스트 참조자까지 모두 복사를 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 &lt;code&gt;copy&lt;/code&gt; 라이브러리의 &lt;code&gt;deepcopy&lt;/code&gt; 메소드를 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;import copy

a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)

b[0][1] = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 복사를 하면 리스트의 내부까지 복사한 새로운 객체를 만들어서 변수에 할당한다. 따라서 내부의 리스트의 id도 새로 생성한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;deepcopy, copy에 따른 id 비교&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;copy4.png&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VYL3B/btsuf0cpfMP/tblPfMo2ErgnAXTx555271/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VYL3B/btsuf0cpfMP/tblPfMo2ErgnAXTx555271/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VYL3B/btsuf0cpfMP/tblPfMo2ErgnAXTx555271/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVYL3B%2Fbtsuf0cpfMP%2FtblPfMo2ErgnAXTx555271%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;730&quot; data-filename=&quot;copy4.png&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 copy로 복사한 경우 얕은 복사가 되며 내부에서 각 리스트의 id를 보면 동일하게 유지되는 것을 볼 수 있다. 이런 구조에서 내부 리스트를 변경하면 그냥 리스트 객체에 참조를 새로 대입한 것과 동일한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;copy5.png&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OlZXT/btsubJo5T0c/6rktkETehN5Wy293LrVNJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OlZXT/btsubJo5T0c/6rktkETehN5Wy293LrVNJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OlZXT/btsubJo5T0c/6rktkETehN5Wy293LrVNJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOlZXT%2FbtsubJo5T0c%2F6rktkETehN5Wy293LrVNJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;584&quot; height=&quot;781&quot; data-filename=&quot;copy5.png&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;code&gt;deepcopy&lt;/code&gt;를 하면 list의 id가 서로 다른 것을 알 수 있다. 이렇게 복사를 하기 때문에 특별한 문제없이 리스트의 변화를 줄 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 파이썬에서 가장 많이 다루는 자료형이지만 생각보다 그 내부를 깊이있게 알고 사용하는 사람은 많지 않다. 이는 딕셔너리, 튜플도 마찬가지인데 리스트는 독립적으로 다룰 필요가 있을만큼 내부구조가 복잡하다. 그래서 이번 글에서는 리스트만 따로 분석을 했다. 다음 포스팀에서는 딕셔너리와 튜플을 알아볼 예정이다. 필요에 따라서는 딕셔너리는 따로 분석할 수도 있다.&lt;/p&gt;</description>
      <category>언어 깊게 파보기/Python</category>
      <category>list</category>
      <category>Python</category>
      <category>깊은복사</category>
      <category>리스트</category>
      <category>메모리</category>
      <category>얕은복사</category>
      <category>파이썬</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/71</guid>
      <comments>https://cow-coding.tistory.com/71#entry71comment</comments>
      <pubDate>Sun, 17 Sep 2023 23:48:46 +0900</pubDate>
    </item>
    <item>
      <title>[파이썬 깊게 파보기] 1. Python의 객체와 변수의 개념</title>
      <link>https://cow-coding.tistory.com/70</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접 과정에서 파이썬과 관련된 질문을 많이 받았는데, 생각보다 내가 파이썬을 잘 모르고 있다는 사실이 상당히 충격으로 다가왔다. 그래서 기본서로 공부할 책과 좀 더 심화적인 스킬들을 공부할 책을 선정해서 파이썬을 깊게 팔 예정이다. 포스팅 시리즈는 &lt;b&gt;Deep Dive&lt;/b&gt; 시리즈로 선정했으며 일반적인 파이썬 기초와는 거리가 있을 것이다. Deep Dive Python 시리즈는 다음과 같은 것들을 중점으로 공부할 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python의 기초 활용보다는 기초에서는 살~짝 벗어난 관점들&lt;/li&gt;
&lt;li&gt;Python의 메모리 단계까지의 관점&lt;/li&gt;
&lt;li&gt;Python을 더 잘 활용하는 방법&lt;/li&gt;
&lt;li&gt;Python의 효율적으로 활용하는 것&lt;/li&gt;
&lt;li&gt;CPython과 공식문서로 자세한 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 읽는 사람이 만약 Python을 처음으로 입문하는 사람이라면 별로 추천하지 않는다. 이 글은 많은 컴퓨터공학적 지식이 기반에 깔려있다고 생각하고 작성하는 글이기 때문이다. 물론 꾸역꾸역 읽으면 어디가서 아는 척하긴 좋겠지만... 딱히 초보자에겐 추천하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deep Dive Python 첫번째 시리즈로 &lt;b&gt;Python의 객체와 변수 개념&lt;/b&gt;에 대해 다룰 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Python의 객체&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Python의 구현체&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Python은 무슨 언어로 구현되어 있을까? Python의 표준 구현체는 C언어로 구현되어 있다. 물론 C++이나 파이썬, Java로 구현된 구현체들도 있지만 표준 구현체는 CPython이고 &lt;a href=&quot;https://github.com/python/cpython&quot;&gt;python의 공식 레포지토리&lt;/a&gt;도 CPython으로 지원한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Python Object&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;pyobject.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgZqMu/btsubJiltTT/Yz1U0mGlJwNmkkKX8M1c6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgZqMu/btsubJiltTT/Yz1U0mGlJwNmkkKX8M1c6K/img.png&quot; data-alt=&quot;나무위키 - 파이썬&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgZqMu/btsubJiltTT/Yz1U0mGlJwNmkkKX8M1c6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgZqMu%2FbtsubJiltTT%2FYz1U0mGlJwNmkkKX8M1c6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;418&quot; data-filename=&quot;pyobject.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나무위키 - 파이썬&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.python.org/3/c-api/concrete.html&quot;&gt;파이썬 공식문서&lt;/a&gt;에 들어가보면 모든 구현체들이 &lt;b&gt;Python Object&lt;/b&gt;라는 객체로 구현된 것을 볼 수 있다. 이렇게 공식문서를 보면 알 수 있듯이 파이썬은 객체 지향으로 개발된 언어라는 것을 알 수 있다. 그래서 보통 Python 자체를 순수 객체지향 언어라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;b&gt;파이썬은 객체 지향 언어&lt;/b&gt;라고 표현하는 것은 조심할 필요가 있다. 물론 파이썬이 객체 지향으로 개발된 언어인 것이므로 틀린 말은 아니지만 객체 지향은 프로그래밍 패러다임이기 때문에 사용자의 사용성에 따라 불리는 것이 달라질 수 있다. 파이썬은 절차 지향으로 프로그래밍을 배우기도 하고 상황에 따라서는 함수형 프로그래밍도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬의 객체는 크게 &lt;b&gt;가변형(Mutable)&lt;/b&gt; 과 &lt;b&gt;불변형(Immutable)&lt;/b&gt; 로 구분한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가변형 : 리스트, 딕셔너리, 집합(set) 등...&lt;/li&gt;
&lt;li&gt;불변형 : 숫자, 문자열, 튜플 등...&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 특징 때문에 파이썬은 &lt;b&gt;함수에 매개변수를 어떤 것을 전달하냐에 따라&lt;/b&gt; 처리가 달라진다. 불변 객체를 넘겨줄 경우 특수한 처리를 하지 않는다면 Call by Value로 처리하고, 가변 객체를 넘겨주면 Call by Reference로 처리한다. 파이썬 공식문서에서는 &lt;b&gt;Call by Assignment (할당에 의한 호출)&lt;/b&gt; 와 &lt;b&gt;Call by Object Reference (객체 참조에 의한 호출)&lt;/b&gt; 이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수에 대한 얘기는 나중에 자세히 파보도록하고 일단 이번 포스팅에서는 &lt;b&gt;불변 객체 위주의 변수 할당&lt;/b&gt;을 알아보겠다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Python의 변수&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Python의 변수와 객체의 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬이든, C++이든 자바든 많은 언어들은 &lt;code&gt;=&lt;/code&gt; 연산자를 활용해서 변수를 선언한다. C++와 같은 언어는 메모리에 변수가 저장되는 방식으로 관리한다. 새로운 변수에 다른 선언된 변수를 할당하면 새로운 메모리에 동일한 객체가 복사되는 방식으로 저장된다. 그래서 만약 동일 메모리의 변수를 가리키고 싶다면 C++은 &lt;b&gt;포인터(pointer)&lt;/b&gt; 를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 파이썬이 간단해보이지만 꽤 복잡한 언어인 이유가 나타나는데, &lt;a href=&quot;https://github.com/python/cpython/tree/main/Objects&quot;&gt;CPython의 객체 코드&lt;/a&gt;를 뜯어보면 많은 포인터들로 구성되어 있다는 것을 알 수 있다. 일단 간단하게 깔고 들어가면, &lt;b&gt;파이썬의 변수 대입(&lt;code&gt;=&lt;/code&gt;)은 새로운 메모리 할당이 아니라 객체에 대한 참조이다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Python 객체의 기초 지식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터 조금 복잡해지는데, 우선 기본적으로 알고 가야하는 파이썬 지식 중 하나는 &lt;b&gt;파이썬에서 -5 ~ 256까지의 수는 이미 만들어진 객체를 할당&lt;/b&gt;한다는 것이다. 이 범위 밖의 정수는 새로 객체를 할당한다. 특이한 점은 같은 257을 두번 할당하면 서로 다른 객체가 선언된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 객체의 주소와 비슷한 격인 내용을 확인하려면 &lt;code&gt;id(변수명)&lt;/code&gt;을 사용하면 객체의 아이덴티티를 알 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Python의 변수 선언&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 리스트를 사용하다보면 아래와 같은 코드를 짜고 난감한 결과를 경험한 적이 있을 것이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a = [1, 2, 3, 4]
b = a

b[0] = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 a의 값은 변화가 될까? 라는 질문을 받으면, C++을 메인으로 사용한 사람의 경우 변하지 않는다고 말할 것이다. 하지만 파이썬은 변하게 된다. 이유는 위에서 언급했던 파이썬의 변수 할당 방식때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;assign.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1DzLd/btsubLmPsje/F6nycG0y25VClHvfmAObz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1DzLd/btsubLmPsje/F6nycG0y25VClHvfmAObz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1DzLd/btsubLmPsje/F6nycG0y25VClHvfmAObz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1DzLd%2FbtsubLmPsje%2FF6nycG0y25VClHvfmAObz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;218&quot; data-filename=&quot;assign.png&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 변수에 값을 복사하는 개념이 아니라 &lt;b&gt;변수가 지시(point)하는 객체가 바뀌는 것&lt;/b&gt;이다. 가끔 그런 생각해본 적 없는가? &lt;b&gt;&quot;파이썬은 왜 자료형을 선언하는 방식으로 변수를 선언하지 않지?&quot;&lt;/b&gt;&lt;br /&gt;파이썬에서 변수의 역할을 생각해보면 자료형을 굳이 선언할 필요가 없다. 왜냐면 파이썬에서 변수는 그냥 지시하는 객체만 바꾸면 되기 때문이다. 우리가 C++이나 자바에서 자료형을 선언하는 이유는 &lt;b&gt;자료형에 따라 메모리에 할당하는 공간의 크기를 결정하기 때문이다.&lt;/b&gt; 하지만 파이썬은 객체를 생성하고 변수는 객체를 지시만 하면 되므로 그럴 필요가 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 특징과 위에서 언급한 -5 ~ 256까지는 이미 만들어진 객체를 사용한다는 것 때문에 신기한 결과가 나타난다. 같은 257인데, 다르다고 나올 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동일성 판단 (&lt;code&gt;is&lt;/code&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서는 동일성을 판단하는 연산자가 크게 2가지 종류가 있다. &lt;code&gt;==&lt;/code&gt;와 &lt;code&gt;is&lt;/code&gt; 연산자이다. 일반적으로 프로그래밍 언어는 불필요한 연산자와 기능을 굳이 만들지 않는다. 그렇다면 얼핏 보기에 비슷한 두 연산자가 왜 있을까? 그 이유는 당연하게도 역할이 다르기 때문이다.&lt;br /&gt;자바스크립트를 써 본 사람이라면 &lt;code&gt;==&lt;/code&gt;과 &lt;code&gt;===&lt;/code&gt;의 차이점을 알 것이다. 위 두 연산자의 차이도 이것과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;==&lt;/code&gt;은 단순히 &lt;b&gt;객체 값의 동일성&lt;/b&gt;을 판정하고, &lt;code&gt;is&lt;/code&gt;는 &lt;b&gt;객체 자체의 동일성&lt;/b&gt;을 판정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 코드의 출력결과는 어떻게 될까?&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a = 3
b = 3

c = 257
d = 257

print(a == b)
print(a is b)
print(c == d)
print(c is d)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 눈치 빠른 사람들은 &lt;code&gt;T&lt;/code&gt; &lt;code&gt;T&lt;/code&gt; &lt;code&gt;T&lt;/code&gt; &lt;code&gt;F&lt;/code&gt;라는 것을 알 수 있다. 근데 왜...? 객체가 같지/다르지? 라는 생각이 들 수 있다. 이유는 -5 ~ 256은 이미 만들어진 객체를, 범위 밖의 숫자는 새로 객체를 할당한다. 이런 이유때문에 3은 같게, 257은 다르게 나온다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;del&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 제거하는 연산, 특히 dictionary나 리스트에서 제거할때, &lt;code&gt;del&lt;/code&gt; 연산자를 사용하는 것을 본 사람들이 있을 것이다. 이름이 &lt;code&gt;del&lt;/code&gt;이라서 처음 배운 언어가 뭐냐에 따라 메모리 할당 제거를 하는 것이라 생각할 수 있으나 &lt;code&gt;del&lt;/code&gt;의 역할은 &lt;b&gt;변수와 객체의 연결관계를 분리하는&lt;/b&gt; 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 여기서 사용되지 않는 객체는 어떻게 관리될까?&lt;br /&gt;파이썬은 &lt;a href=&quot;https://github.com/python/cpython/blob/ef61b259e35a0249840184b59f43d8a7f9b095bc/Include/object.h#L102-L106&quot;&gt;몇 개의 변수가 객체를 참조하고 있는지 관리하는 변수인 &lt;code&gt;ob_refcnt&lt;/code&gt;&lt;/a&gt;가 있다. 참조 변수가 만약 0이 된다면 파이썬은 일정 시간 후에 메모리를 해제하는 코드를 동작시킨다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
{
    _Py_DECREF_STAT_INC();
    _Py_RefTotal--;
    if (--op-&amp;gt;ob_refcnt != 0) {
        if (op-&amp;gt;ob_refcnt &amp;lt; 0) {
            _Py_NegativeRefcount(filename, lineno, op);
        }
    }
    else {
        _Py_Dealloc(op);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식을 써서 파이썬은 메모리를 관리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 변수만 봐도 굉장히 깊게 공부할 요소들이 많다. 이 부분은 파이썬이 동작하는 과정에서 중요한 부분이다. 변수 컨트롤을 진행하는 요소나 서비스를 개발하는 과정에서 리소스 관리에서도 중요하게 작용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 불변 객체 위주로 변수를 봤고 다음 포스팅은 가변 객체인 리스트, 딕셔너리, set의 구조와 동작에 대해서 다뤄볼 예정이다.&lt;/p&gt;</description>
      <category>언어 깊게 파보기/Python</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/70</guid>
      <comments>https://cow-coding.tistory.com/70#entry70comment</comments>
      <pubDate>Sun, 17 Sep 2023 23:46:00 +0900</pubDate>
    </item>
    <item>
      <title>부스트캠프 글은 아래 링크로 이관되었습니다.</title>
      <link>https://cow-coding.tistory.com/69</link>
      <description>&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://cow-coding.github.io/categories/naver-boostcamp-ai-tech/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://cow-coding.github.io/categories/naver-boostcamp-ai-tech/&lt;/a&gt;&lt;/h3&gt;</description>
      <category>네이버 부스트캠프 AI Tech/부캠 글은 이관되었습니다</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/69</guid>
      <comments>https://cow-coding.tistory.com/69#entry69comment</comments>
      <pubDate>Sun, 17 Sep 2023 23:32:52 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스/MySQL] Lv.2 자동차 평균 대여 기간 구하기</title>
      <link>https://cow-coding.tistory.com/68</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/157342&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/157342&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694960506251&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/157342&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/r2VIt/hyTY2BteWT/NDIV9M4k0Uj6idA5JtQhQk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/U7219/hyTV3hKbZ7/R3xzQHOVFs9wuJQ0XVBU4K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/157342&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/157342&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/r2VIt/hyTY2BteWT/NDIV9M4k0Uj6idA5JtQhQk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/U7219/hyTV3hKbZ7/R3xzQHOVFs9wuJQ0XVBU4K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;해설&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡하게 푸는 방법이 있고 적당히 잘 하나의 쿼리로 처리하는 방법이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 중 어떤게 더 성능상 유리하다고 말씀드리긴 어렵지만 저는 GROUP BY 를 사용해서 풀이를 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브쿼리를 이용해도 가능해보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694960857653&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT CAR_ID,
        ROUND(AVG(DATEDIFF(END_DATE, START_DATE) + 1), 1) AS AVERAGE_DURATION
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
GROUP BY CAR_ID
HAVING AVERAGE_DURATION &amp;gt;= 7
ORDER BY AVERAGE_DURATION DESC, CAR_ID DESC;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜의 기간을 계산하는 쿼리에는 DATE_DIFF 가 있습니다. 이걸 사용하실때 주의할 점은 정확한 기간차이를 구할때는 반드시 +1을 해줘야 한다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 때문에 좀 고생한 문제가 있었습니다.&lt;/p&gt;</description>
      <category>코딩테스트(알고리즘, SQL)/프로그래머스</category>
      <category>MySQL</category>
      <category>데이터엔지니어</category>
      <category>코딩테스트</category>
      <category>프로그래머스</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/68</guid>
      <comments>https://cow-coding.tistory.com/68#entry68comment</comments>
      <pubDate>Sun, 17 Sep 2023 23:29:08 +0900</pubDate>
    </item>
    <item>
      <title>2022년 회고</title>
      <link>https://cow-coding.tistory.com/67</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;f5804a456af1f8bb619956e903164159.jpeg&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6Oea2/btr0dlq4R9z/nrqXk9qtBBP9wzOFAtjjNk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6Oea2/btr0dlq4R9z/nrqXk9qtBBP9wzOFAtjjNk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6Oea2/btr0dlq4R9z/nrqXk9qtBBP9wzOFAtjjNk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6Oea2%2Fbtr0dlq4R9z%2FnrqXk9qtBBP9wzOFAtjjNk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;346&quot; data-filename=&quot;f5804a456af1f8bb619956e903164159.jpeg&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 많이 늦은 것 같지만... 2022년은 저에게 있어서 잊지 못할 시간들이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 늦게라도, 짧게라도 2022년의 회고를 한번 기록해보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상반기 (1월 ~ 6월)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년의 상반기는 네이버 부스트캠프 AI Tech와 보냈던 시기였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대학 1학년부터 데이터 관련 직군을 희망해오던 저는 3학년때 머신러닝 엔지니어 혹은 데이터 엔지니어가 되는 것을 목표로 결정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결이 다른 두 직군이지만 그렇다고 또 너무 멀지도 않은 두 직군 사이에서 고민하고 있었고 이는 부스트캠프를 하면서 결정할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;bc3_pass.png&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tYEy8/btr0aUAwH84/8NR5mnXTDqK3LvA2cQg9M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tYEy8/btr0aUAwH84/8NR5mnXTDqK3LvA2cQg9M1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tYEy8/btr0aUAwH84/8NR5mnXTDqK3LvA2cQg9M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtYEy8%2Fbtr0aUAwH84%2F8NR5mnXTDqK3LvA2cQg9M1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1368&quot; height=&quot;306&quot; data-filename=&quot;bc3_pass.png&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운명이었던건지 아니면 제가 관심을 갖고 트렌드를 읽어서 생긴 기회인지는 모르겠지만 정말 적기에 부스트캠프에서 추천시스템 트랙이 열렸습니다. 혼자서 공부하기에는 너무나도 어려웠고 자료도 없었는데 공부를 할 수 있는 로드맵을 제공해주는 것만으로도 너무 기대가 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Leve 1과 Level 2 기간동안 절대 놓치지 않았던 것들이 있었습니다. 적어도 강의에서 나온 내용을 최대한 &lt;b&gt;내 방식으로 기록하자 &lt;/b&gt;였습니다. 제가 부스트캠프를 시작했던 이유는 &lt;b&gt;내가 하기로 한 분야에 대해서 대충도 설명 못하는데 어떻게 그 분야에서 일할 수 있겠어?&lt;/b&gt; 라는 생각때문이었습니다. 이런 의지로 되도록 그날 공부한 내용들을 최대한 제가 이해한 방식으로 정리를 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그날 그날 회고를 꾸준히 기록했습니다. 정말 큰거부터 정리 시간이 오래 걸린다는 사소한 부분까지 기록했습니다. 물론 P-stage와 Product Serving에서는 너무 개인 회고를 적는거 보다 프로젝트 개발, 실험 일지를 적는걸로 대체했습니다. 공부보다 실제 프로젝트를 진행하는 과정에서 접한 느낌들을 위주로 정리했습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자세한 포스트를 보고 싶으시다면 아래의 링크로 가시면 보실 수 있습니다. (시간이 나는 대로 티스토리로 블로그를 이전할 생각입니다.)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://cow-coding.github.io/categories/naver-boostcamp-ai-tech/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://cow-coding.github.io/categories/naver-boostcamp-ai-tech/&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;figure id=&quot;og_1676988072308&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;NAVER BoostCamp AI Tech&quot; data-og-description=&quot;A minimal, portfolio, sidebar, bootstrap Jekyll theme with responsive web design and focuses on text presentation.&quot; data-og-host=&quot;cow-coding.github.io&quot; data-og-source-url=&quot;https://cow-coding.github.io/categories/naver-boostcamp-ai-tech/&quot; data-og-url=&quot;https://cow-coding.github.io/categories/naver-boostcamp-ai-tech/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eVXOi/hyRIpMGzB0/8RQG35449TPEZ8KKbrBncK/img.jpg?width=263&amp;amp;height=263&amp;amp;face=0_0_263_263&quot;&gt;&lt;a href=&quot;https://cow-coding.github.io/categories/naver-boostcamp-ai-tech/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cow-coding.github.io/categories/naver-boostcamp-ai-tech/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eVXOi/hyRIpMGzB0/8RQG35449TPEZ8KKbrBncK/img.jpg?width=263&amp;amp;height=263&amp;amp;face=0_0_263_263');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;NAVER BoostCamp AI Tech&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A minimal, portfolio, sidebar, bootstrap Jekyll theme with responsive web design and focuses on text presentation.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cow-coding.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부스트캠프 과정을 진행하면서 머신러닝 엔지니어보다는 데이터 엔지니어가 맞겠다는 생각을 했습니다. 그런 생각이 들었던 이유는 여러가지가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;머신러닝을 실제로 적용하는 것과 공부하는 것에는 괴리가 존재한다.&lt;/li&gt;
&lt;li&gt;특히 추천시스템을 적용시키는 것은 생각보다 현실과의 타협이 있기도 한다.&lt;/li&gt;
&lt;li&gt;이제는 코드보다는 데이터가 성능을 결정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 생각이 제가 데이터 엔지니어로 진로를 선택한 이유였습니다. 물론 지금은 세상을 뜨겁게 달구고 있는 ChatGPT가 나오면서 첫번째 생각이 조금은 달라질 수 있어보이지만... 그래도 그런 대규모 모델은 현실에 적용하는 데에 상당한 리소스와의 타협이 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 머신러닝이 미래 전망이 있어보인다는 생각을 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 단순히 연구 중심의 모델이 나왔다면 이제는&amp;nbsp;&lt;b&gt;서비스에 적용되는 AI&lt;/b&gt;가 슬슬 고도화 될 시기라고 생각했습니다. 그런 점에 있어서 모델을 서빙하는 것이 중요해지고 이에 맞춰 MLOps의 중요도가 높아질 것이라고 생각했기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부스트캠프 마지막에는 서비스 개발을 하면서 PM과 동시에 데이터 수집을 맡았습니다. 그 과정에서 비동기로 데이터를 처리하는 방법, 도커를 띄워서 MSA 방식으로 설계를 하는 것과 같은 &lt;b&gt;여러가지 시도와 경험&lt;/b&gt;을 했습니다.&amp;nbsp;&lt;s&gt;물론 취업을 한 지금 상황에서 보면 참 정교함 없이 한 거처럼 보이지만요&lt;br /&gt;&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 서비스 개발 일지를 보고 싶으시다면 아래 링크를 확인해주세요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cow-coding.github.io/categories/level-3-final-project/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://cow-coding.github.io/categories/level-3-final-project/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676988620594&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Level 3 - Final Project&quot; data-og-description=&quot;A minimal, portfolio, sidebar, bootstrap Jekyll theme with responsive web design and focuses on text presentation.&quot; data-og-host=&quot;cow-coding.github.io&quot; data-og-source-url=&quot;https://cow-coding.github.io/categories/level-3-final-project/&quot; data-og-url=&quot;https://cow-coding.github.io/categories/level-3-final-project/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b7vYSp/hyRHvgFYBx/AowuJKxaSlMO5lscRbBQ20/img.jpg?width=263&amp;amp;height=263&amp;amp;face=0_0_263_263&quot;&gt;&lt;a href=&quot;https://cow-coding.github.io/categories/level-3-final-project/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cow-coding.github.io/categories/level-3-final-project/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b7vYSp/hyRHvgFYBx/AowuJKxaSlMO5lscRbBQ20/img.jpg?width=263&amp;amp;height=263&amp;amp;face=0_0_263_263');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Level 3 - Final Project&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A minimal, portfolio, sidebar, bootstrap Jekyll theme with responsive web design and focuses on text presentation.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cow-coding.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;하반기 (7월 ~ 12월)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하반기는 부스트캠프를 마치고 취업 준비와 인턴생활이 주를 이뤘습니다. 시간이 조금 지나서 그때 어떤 기업들을 넣었는지 잘 기억은 안나지만 네이버(전환형 인턴), 라인(정규직), 네이버 웹툰(체험형 인턴), 카카오(전환형 인턴), 오늘의 집(공채), 토스(공채)를 넣었던 걸로 기억합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 먼저 말씀드리자면...&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1차 탈-네이버, 네이버 웹툰, 라인, 오늘의 집, 토스&lt;/li&gt;
&lt;li&gt;최종합 - 카카오&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업준비를 하는 과정과 제 포트폴리오에 대한 내용은 나중에 따로 포스팅을 하겠습니다. (최근 업무때문에 과연 언제가 될 지...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 한 우물만 파던 4수생은, 9월에 원하던 데이터 직군의 데이터 엔지니어로 합격을 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-02-21 오후 11.16.09.png&quot; data-origin-width=&quot;1736&quot; data-origin-height=&quot;1468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drGt3Q/btr0klP1bEv/3sh8MN77FrQPpzpy0RIKA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drGt3Q/btr0klP1bEv/3sh8MN77FrQPpzpy0RIKA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drGt3Q/btr0klP1bEv/3sh8MN77FrQPpzpy0RIKA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrGt3Q%2Fbtr0klP1bEv%2F3sh8MN77FrQPpzpy0RIKA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;423&quot; data-filename=&quot;스크린샷 2023-02-21 오후 11.16.09.png&quot; data-origin-width=&quot;1736&quot; data-origin-height=&quot;1468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 참 신기한게 있습니다. 제가 4수 끝에 대학교에 들어와서 데이터 직군이라는 방향을 잡을 때 가장 많이 도움을 받은 글이 페이스북의 카카오 인재영입 글이었습니다. 그랬던 학생이 이제는 카카오 데이터 엔지니어가 됐습니다. 감회가 새롭다는 말이 이럴때 필요한 말이지 않을까 싶네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인턴생활이 쉽지만은 않았습니다. 제가 소속한 팀인 데이터정보플랫폼팀은 저희 팀 팀장님인 &lt;a href=&quot;https://tech.kakao.com/2022/06/16/data-engineering/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;kenny가 작성하신 글&lt;/a&gt;에 나온 내용처럼 데이터를 모아서 정보를 제공하는 플랫폼을 개발하는 팀입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러니까 플랫폼을 만들어야 하는 것이죠. 많은 사람들이 데이터 엔지니어는 하둡, 스파크, airflow 같은 데이터 엔지니어링 툴만 사용한다는 오해를 갖고 있습니다. 하지만 데이터 엔지니어는 그렇게 수집된 데이터를 필요에 따라서는 화면으로 제공을 해주기도 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀에 들어와서 React, Flask 같은 기술들을 접하고 동시에 데이터 처리를 위한 Spark, Scala 등의 기술들도 익혔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 양의 기술스택을 한번에 익혀야 했다보니 너무나도 힘들었지만 같은 팀 동료들과 저희 파트장님이 최대한 배려를 해주셔서 잘 마칠 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 길다면 길고 짧다면 짧은 3개월의 인턴이 끝나고 최종 평가 발표를 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 지난해 12월 26일 정직원 전환이 이뤄졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0481.jpg&quot; data-origin-width=&quot;1509&quot; data-origin-height=&quot;947&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSO8RR/btr0glwzZ4P/RkSGEsyjZS7THKBOpUbBm0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSO8RR/btr0glwzZ4P/RkSGEsyjZS7THKBOpUbBm0/img.jpg&quot; data-alt=&quot;인턴때도 명한을 발급받을 수는 있습니다 ㅎㅎ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSO8RR/btr0glwzZ4P/RkSGEsyjZS7THKBOpUbBm0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSO8RR%2Fbtr0glwzZ4P%2FRkSGEsyjZS7THKBOpUbBm0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;251&quot; data-filename=&quot;IMG_0481.jpg&quot; data-origin-width=&quot;1509&quot; data-origin-height=&quot;947&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인턴때도 명한을 발급받을 수는 있습니다 ㅎㅎ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;13살에 개발자의 꿈을 꿨던 꼬마가 13년의 세월이 지나서 개발자가 되었고 23살에 참고했던 카카오의 데이터 직군 정보를 보고 데이터 직군을 결정한 대학생은 3년 뒤 카카오 데이터 직군에 들어왔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정직원이 되고, 맡은 업무 개발을 진행한지 어느덧 6개월이 지났습니다. 이제는 처음 본 React, Spark가 어느정도 익숙해졌지만 아직도 갈 길이 멀다고 생각합니다. 단순히 기술스택만이 아닌 인프라 기술에서는 아직도 모르겠는 것들이 투성인 거 같네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 팀원들에게도 배울 점이 너무 많고 취업을 하고 나니 오히려 부족한 점이 더 많다고 느껴집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주니어 데이터 엔지니어의 길이 이제 시작되었고 개인적으로 앞으로가 기대가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회고라고 하고 의식의 흐름이라고 불러야 하는게 맞는 글인듯 싶습니다.&lt;/p&gt;</description>
      <category>Life</category>
      <category>2022</category>
      <category>데이터엔지니어</category>
      <category>머신러닝</category>
      <category>부스트캠프</category>
      <category>취업</category>
      <category>카카오</category>
      <category>회고</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/67</guid>
      <comments>https://cow-coding.tistory.com/67#entry67comment</comments>
      <pubDate>Tue, 21 Feb 2023 23:36:31 +0900</pubDate>
    </item>
    <item>
      <title>블로그 관리 프로젝트</title>
      <link>https://cow-coding.tistory.com/66</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;부스트캠프 끝나고 시간이 좀 흐르고 지금 취업준비를 하는 과정에서 블로그 이동을 고려해 볼 때가 온 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 목적이 있다기 보다는 현재 깃허브 블로그가 구글 색인 생성이 잘 이뤄지지 않는 것으로 보인다. 그래서&amp;nbsp;요 근래 공부하면서 블로그에 작성하는 양이 늘었는데 조금은 더 안정적인 티스토리에 글을 옮기려고 생각이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 이거 옮기면 벌써 3번째 이동인데... 이제 블로그 글도 많아져서 자리를 잡아야 할 것으로 보인다. 깃허브 블로그를 쓰면 있어보인다는 장점이 있는데 관리가 너무 힘들다. 솔직히 공부도 힘든데 직접 블로그 관리까지... 이건 진짜 골머리가 아프다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 블로그 관리 및 이전 프로젝트는 다음과 같이 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 현재 깃허브 테마 변경 (변성윤 마스터님 테마로)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-1. 만약 문제 없으면 그대로 깃허브 블로그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-2. 문제 생기면 티스토리로 블로그 글 전부 이전&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 블로그로 글 이전하면 진짜 한참 걸릴거 같다. 날 잡아서 싹 옮기든가 해야지&lt;/p&gt;</description>
      <category>Life</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/66</guid>
      <comments>https://cow-coding.tistory.com/66#entry66comment</comments>
      <pubDate>Wed, 27 Jul 2022 23:37:40 +0900</pubDate>
    </item>
    <item>
      <title>[coding test] 카카오 블라인드 2021 - 신규 아이디 추천</title>
      <link>https://cow-coding.tistory.com/61</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/72410&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/72410&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1634198891675&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 신규 아이디 추천&quot; data-og-description=&quot;카카오에 입사한 신입 개발자 네오는 &amp;quot;카카오계정개발팀&amp;quot;에 배치되어, 카카오 서비스에 가입하는 유저들의 아이디를 생성하는 업무를 담당하게 되었습니다. &amp;quot;네오&amp;quot;에게 주어진 첫 업무는 새로 &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/72410&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/72410&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bNZmrb/hyLXFa7OhE/eYopuaDKhpjBLFzKS9OHJ1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/mlLGw/hyLYyapBsO/yAA3E9gqapoecHks2v3hm1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/72410&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/72410&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bNZmrb/hyLXFa7OhE/eYopuaDKhpjBLFzKS9OHJ1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/mlLGw/hyLYyapBsO/yAA3E9gqapoecHks2v3hm1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 신규 아이디 추천&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오에 입사한 신입 개발자 네오는 &quot;카카오계정개발팀&quot;에 배치되어, 카카오 서비스에 가입하는 유저들의 아이디를 생성하는 업무를 담당하게 되었습니다. &quot;네오&quot;에게 주어진 첫 업무는 새로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;문제 Comment&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오 블라인드 코딩 테스트의 요즘 트렌드는 &quot;구현&quot;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매년 인턴십, 블라인드 코테에 이런식으 조건을 부여하고 구현하는 문제들이 꼭 1문제씩은 출제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제를 접근할 때는 문제상에 구현의 힌트를 많이 주기때문에 문제와 주어진 예시를 잘 파악하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;문제 풀이&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열을 control하는 문제인 만큼 파이썬이 유리할 수도 있지만 C++로 접근해도 충분히 괜찮은 문제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 풀이 접근에서 치환과 추가는 어렵지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 제거의 효율을 높이고자 제거되는 문자열은 '\0'로 변경해주었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;지금 생각해보면 그렇게 효율이 좋은것 같지는 않은 것 같기도...&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++의 문자열에서 '\0'은 출력상에 표현하지 않기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 여기서 주의할 점은 string을 비교할 때, 중간에 들어간 \0은 유지가 되므로 출력이 같은 문자열이 서로 다른 문자열로 인식됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634199331830&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string str1 = &quot;abcd&quot;;
string str2 = &quot;a\0bcd&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우 C++의 채점서버는 두 문자열이 다르다고 채점을 하고 심지어 두 문자열의 길이도 str1은 4, str2는 5로 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 길이와 관련된 작업이 있거나 제거 작업이 끝난 후에는 반드시 \0을 빼고 문자열을 재구성할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1634200520201&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;

string reset(string input) {
    string ret = &quot;&quot;;
    
    for(int i = 0; i &amp;lt; input.size(); i++) {
        if(input[i] != '\0') ret += input[i];
    }
    
    return ret;
}

string solution(string new_id) {
    string answer = &quot;&quot;;
    char m[3] = {'-', '_', '.'};
    
    // step 1
    for(int i = 0; i &amp;lt; new_id.size(); i++) {
        new_id[i] = tolower(new_id[i]);
    }
    
    // step 2
    for(int i = 0; i &amp;lt; new_id.size(); i++) {
        bool flag = false;
        
        if(new_id[i] &amp;lt; 'a' || new_id[i] &amp;gt; 'z') {
            if(new_id[i]-'0' &amp;gt;= 0 &amp;amp;&amp;amp; new_id[i]-'0' &amp;lt;= 9) continue;
            else {
                for(int j = 0; j &amp;lt; 3; j++) {
                    if(new_id[i] == m[j]) {
                        flag = true;
                        break;
                    }
                }
            
                if(!flag) {
                    new_id[i] = '\0';
                }
            }
        }
    }
    new_id = reset(new_id);
    
    char prev = new_id[0];
    
    // step 3
    for(int i = 1; i &amp;lt; new_id.size(); i++) {
        if(prev == '.' &amp;amp;&amp;amp; new_id[i] == '.') {
            prev = new_id[i];
            new_id[i] = '\0';
        }else{
            prev = new_id[i];
        }
    }
    new_id = reset(new_id);
    
    // step 4
    if(new_id[0] == '.') new_id[0] = '\0';
    if(new_id[new_id.size() - 1] == '.') new_id[new_id.size() - 1] = '\0';
    new_id = reset(new_id);
    
    // step 5
    if(new_id.size() == 0) new_id += 'a';
    
    
    // step 6
    if(new_id.size() &amp;gt; 15) {
        for(int i = 15; i &amp;lt; new_id.size(); i++) {
            new_id[i] = '\0';
        }
    }
    new_id = reset(new_id);
    
    // step 4
    if(new_id[0] == '.') new_id[0] = '\0';
    if(new_id[new_id.size() - 1] == '.') new_id[new_id.size() - 1] = '\0';    
    new_id = reset(new_id);
    
    // step 7
    if(new_id.size() &amp;lt; 3) {
        while(new_id.size() != 3) {
            new_id += new_id[new_id.size() - 1];
        }
    }
    
    

    answer = new_id;
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;step 6를 종료한 후 양 끝의 마침표를 제거해야하므로 step4를 반드시 다시 한 번 해주셔야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩테스트(알고리즘, SQL)/프로그래머스</category>
      <category>kakao blind recruitment</category>
      <category>kako</category>
      <category>구현</category>
      <category>알고리즘</category>
      <category>카카오 코딩테스트</category>
      <category>카카오 코테</category>
      <category>코딩테스트</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/61</guid>
      <comments>https://cow-coding.tistory.com/61#entry61comment</comments>
      <pubDate>Thu, 14 Oct 2021 17:40:10 +0900</pubDate>
    </item>
    <item>
      <title>[TS] 시계열 데이터 정리와 기본 개념</title>
      <link>https://cow-coding.tistory.com/59</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 글은 책 '실전 시계열 분석'을 재구성하여 작성되었습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&amp;amp;mallGb=KOR&amp;amp;barcode=9791162244081&amp;amp;orderClick=LEa&amp;amp;Kc=&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;책링크&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1626360727359&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;book&quot; data-og-title=&quot;실전 시계열 분석&quot; data-og-description=&quot;실전 시계열 분석: 통계와 머신러닝을 활용한 예측 기법시계열 분석의 모든 것실제 환경에 특화된 시계열 데이터 분석 및 모범 사례를 다루는 실무 지침서다. ARIMA 및 베이즈 상태 공간 같은 표&quot; data-og-host=&quot;books.google.co.jp&quot; data-og-source-url=&quot;https://books.google.co.kr/books/about/실전_시계열_분석.html?id=yLolEAAAQBAJ&amp;amp;source=kp_book_description&amp;amp;redir_esc=y&quot; data-og-url=&quot;https://books.google.com/books/about/%EC%8B%A4%EC%A0%84_%EC%8B%9C%EA%B3%84%EC%97%B4_%EB%B6%84%EC%84%9D.html?hl=ko&amp;amp;id=yLolEAAAQBAJ&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c7NTxf/hyKT1Fnv84/RdpyE3HgPuZYxwEjS6GeB0/img.jpg?width=128&amp;amp;height=164&amp;amp;face=0_0_128_164&quot;&gt;&lt;a href=&quot;https://books.google.co.kr/books/about/실전_시계열_분석.html?id=yLolEAAAQBAJ&amp;amp;source=kp_book_description&amp;amp;redir_esc=y&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://books.google.co.kr/books/about/실전_시계열_분석.html?id=yLolEAAAQBAJ&amp;amp;source=kp_book_description&amp;amp;redir_esc=y&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c7NTxf/hyKT1Fnv84/RdpyE3HgPuZYxwEjS6GeB0/img.jpg?width=128&amp;amp;height=164&amp;amp;face=0_0_128_164');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;실전 시계열 분석&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실전 시계열 분석: 통계와 머신러닝을 활용한 예측 기법시계열 분석의 모든 것실제 환경에 특화된 시계열 데이터 분석 및 모범 사례를 다루는 실무 지침서다. ARIMA 및 베이즈 상태 공간 같은 표&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;books.google.co.jp&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Data Handling&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. Missing value&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시계열만이 아닌 대부분의 빅데이터에는 결측치가 존재하는 경우가 많습니다. 다양한 이유에서 값이 누락되는 경우가 발생하는 것이죠. 간단한 이유로는 금융사는 고객의 개인정보를 암호화하여 일정한 형태로 저장하는 경우도 있지만 공개되는 데이터에는 값을 제외하는 경우도 많습니다. 이런 다양한 이유로 데이터에 결측치가 발생하는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결측치를 제거하고 데이터를 처리해도 되지만 반드시 괜찮다는 보장은 할 수 없습니다. 만약 결측치가 애매한 비율로 존재하지만 꽤 중요한 정보인 경우에는 결측치가 있는 attribute를 살려야 하기 때문이죠. 그래서 통계학적 방법에는 다양한 결측치 처리 방식이 연구되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 대치법 (Imputation)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 포워드 필 (Forward Fill) : 결측치의 직전 값으로 결측치를 채우는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;850&quot; data-filename=&quot;forward_fill.png&quot; width=&quot;446&quot; height=&quot;393&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dioy6u/btraMpuXp1E/KA8YNsikBN5e6C0ZfWKovk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dioy6u/btraMpuXp1E/KA8YNsikBN5e6C0ZfWKovk/img.png&quot; data-alt=&quot;붉은색 점선은 forward fill 그래프이고 역삼각형은 포워드 필로 채워진 데이터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dioy6u/btraMpuXp1E/KA8YNsikBN5e6C0ZfWKovk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdioy6u%2FbtraMpuXp1E%2FKA8YNsikBN5e6C0ZfWKovk%2Fimg.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;850&quot; data-filename=&quot;forward_fill.png&quot; width=&quot;446&quot; height=&quot;393&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;붉은색 점선은 forward fill 그래프이고 역삼각형은 포워드 필로 채워진 데이터&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프에서 볼 수 있듯이 포워드 필은 원본 데이터의 성향을 띄는 경향이 강합니다. 직전 값으로 결측치를 채우는 방식을 포워드 필, 직후의 값으로 결측치를 채우는 것을 백워드 필 (Backward fill)이라고 합니다. 백워드 필은 일반적으로 미래보다 과거를 채우는 게 더 의미가 있는 경우에 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포워드 필은 계산이 간단하다는 장점이 있고 직전값을 바로 활용하하기 때문에 실시간 스트리밍 데이터에 유리하다는 장점이 있습니다. 그리고 바로 직전 값을 쓰므로 대치작업이 수월하고 코드도 간단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책에는 R코드로 작성되어 있지만 파이썬으로 변환하는 작업을 하고 있기 때문에 파이썬코드를 올려드리겠습니다. 원본 코드는 깃허브 링크를 참조해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627479130228&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import interpolate

# forward fill
rand_unemp['impute.ff'] = rand_unemp.UNRATE.fillna(method='ffill')
bias_unemp['impute.ff'] = bias_unemp.UNRATE.fillna(method='ffill')

fig, ax = plt.subplots(figsize=(12,7))
# origianl graph
sns.scatterplot(data=unemp[350:400], x='DATE', y='UNRATE')
sns.lineplot(data=unemp[350:400], x='DATE', y='UNRATE')
# random missing value graph
sns.lineplot(data=rand_unemp[350:400], x='DATE', y='impute.ff')
sns.scatterplot(data=rand_unemp[350:400][rand_unemp['rpt'] == True], x='DATE', y='impute.ff', marker='v', s=100)

plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 판다스의 read_csv 모듈을 활용해서 적당하게 불러오고 일부러 결측치를 만들었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 참조는 forward fill 주석 부분을 확인하시면 됩니다. 파이썬에는 편리하게 &lt;code&gt;fillna()&lt;/code&gt; 메소드가 있습니다. 내부에 ffill을 파라미터로 넘겨주면 자동으로 포워드 필의 형태를 띕니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이동평균 (moving average) : 최근 과거의 여러 시간대의 내용을 활용하여 채우는 방법 (평균, 중앙값 등을 활용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;850&quot; data-filename=&quot;moving_avg.png&quot; width=&quot;650&quot; height=&quot;587&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qDoFK/btraMpPtTf4/koKkYYfTMMtFDkof3rzk3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qDoFK/btraMpPtTf4/koKkYYfTMMtFDkof3rzk3K/img.png&quot; data-alt=&quot;빨간 선은 사전관찰이 있는 경우, 초록 선은 사전관찰이 없는 경우입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qDoFK/btraMpPtTf4/koKkYYfTMMtFDkof3rzk3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqDoFK%2FbtraMpPtTf4%2FkoKkYYfTMMtFDkof3rzk3K%2Fimg.png&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;850&quot; data-filename=&quot;moving_avg.png&quot; width=&quot;650&quot; height=&quot;587&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빨간 선은 사전관찰이 있는 경우, 초록 선은 사전관찰이 없는 경우입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이동평균을 사용할 때는 사전관찰이 들어가는 경우와 들어가지 않는 경우를 구분해서 알아야합니다. 주어진 정보를 이용해서 통계값으로 대치하기 때문이죠. 그래서 사전관찰이 적용되는 경우와 그렇지 않은 경우를 구분해서 확인해봅시다. 정보가 부족할수록 평균치의 오차가 커지기 때문에 실제 그래프와 차이가 발생합니다. 그래서 사전관찰의 여부를 반드시 생각해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627485405402&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# moving average
rand_unemp['impute.rm.nolookahead'] = rand_unemp[['UNRATE']].rolling(3, min_periods=1).mean()
bias_unemp['impute.rm.nolookahead'] = bias_unemp[['UNRATE']].rolling(3, min_periods=1).mean()

rand_unemp['impute.rm.lookahead'] = rand_unemp[['UNRATE']].rolling(3, min_periods=1, center=True).mean()
bias_unemp['impute.rm.lookahead'] = bias_unemp[['UNRATE']].rolling(3, min_periods=1, center=True).mean()

fig, ax = plt.subplots(figsize=(12,7))
# origianl graph
sns.scatterplot(data=unemp[49:108], x='DATE', y='UNRATE')
sns.lineplot(data=unemp[49:108], x='DATE', y='UNRATE', label='original')
# random missing value graph
sns.lineplot(data=rand_unemp[49:108], x='DATE', y='impute.rm.nolookahead', label='no lookahead')
sns.scatterplot(data=rand_unemp[49:108][rand_unemp['rpt'] == True], x='DATE', y='impute.rm.nolookahead', marker='v', s=100)
sns.lineplot(data=rand_unemp[49:108], x='DATE', y='impute.rm.lookahead', color='green', label='lookahead')
sns.scatterplot(data=rand_unemp[49:108][rand_unemp['rpt'] == True], x='DATE', y='impute.rm.lookahead', marker='x', s=100, color='green')

plt.legend()
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 보간법 (interpolation)&amp;nbsp;&lt;/b&gt;: 전체 data를 기하학적 행동에 제한하여 결측치를 결정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 보간법으로는 선형보간법(linear interpolation)이 있습니다. 선형보간법은 결측치가 주변 데이터에 대해서 선형적인 일관성을 갖게됩니다. 상황에 따라 선형성을 지니는게 원본 데이터와 근접하게 나오는 경우도 많습니다. 그래서 이런 보간법을 사용해서 결측치를 채웁니다. 이때 이동평균과 마찬가지로 과거, 미래 데이터의 활용 여부를 결정하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미래 데이터를 활용하는 경우에는 사전관찰에 주의해줘야 합니다. 미래 데이터가 사용되면 선형성에 영향을 끼칠 수 있기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 사전관찰이 데이터에 영향을 크게 미칠 수 있는지 확인을 반드시 해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;820&quot; data-filename=&quot;interpolation.png&quot; width=&quot;645&quot; height=&quot;470&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwGLVu/btraHrOCLlM/S6sWDhdhcRbrg1Qu7TBYV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwGLVu/btraHrOCLlM/S6sWDhdhcRbrg1Qu7TBYV0/img.png&quot; data-alt=&quot;초록색은 spline interpolation, 빨간색은 liner interpolation&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwGLVu/btraHrOCLlM/S6sWDhdhcRbrg1Qu7TBYV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwGLVu%2FbtraHrOCLlM%2FS6sWDhdhcRbrg1Qu7TBYV0%2Fimg.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;820&quot; data-filename=&quot;interpolation.png&quot; width=&quot;645&quot; height=&quot;470&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;초록색은 spline interpolation, 빨간색은 liner interpolation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보간법은 데이터의 추세를 알고 있는 경우에는 이동평균보다 데이터를 더 잘 대치할 수 있습니다. 하지만 강수량, 수면 시간처럼 비선형적 추세를 갖는 데이터에는 적절하진 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627486156956&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# linear interpolation
tmp1 = rand_unemp.set_index('DATE')
tmp2 = bias_unemp.set_index('DATE')
rand_unemp['impute.li'] = tmp1['UNRATE'].interpolate().values
bias_unemp['impute.li'] = tmp2['UNRATE'].interpolate().values&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;interpolation 메소드를 사용하면 간단하게 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. Sampling&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 불규형이 발생하는 경우가 있습니다. 이런 경우에는 2가지 방식으로 데이터 균형을 맞출 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운샘플링과 업샘플링이 있습니다. 간단하게 다운샘플링부터 알아봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 다운샘플링 (down sampling)&amp;nbsp;&lt;/b&gt;: 데이터의 빈도를 줄이는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 부분집합을 형성하는 방식입니다. 위에 사용한 결측치 샘플은 일부 데이터를 추출해서 만든 다운샘플링 데이터입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운샘플링은 다음과 같은 경우에 자주 이뤄집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;a)&lt;/b&gt;&lt;i&gt; 원본 데이터의 시간 단위가 실용적이지 않는 경우&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 단위가 너무 빈번하게 되는 경우에 다운 샘플링을 진행합니다. 만약 온도를 측정한 데이터가 초단위로 기록된 경우라면 불필요하게 많은 데이터가 기록되어 있습니다. 어떻게보면 세밀한 변화를 기록해서 더 좋은게 아닐까 싶은 생각을 할 수 있습니다. 하지만 데이터가 빈번하게 기록되어 있다는 것은 오차 빈도도 간격만큼 발생할 위험이 있다는 것입니다. 그래서 몇개의 데이터만 추출해서 데이터 자체를 감소할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;b)&amp;nbsp;&lt;/b&gt;&lt;i&gt;계절 주기의 특정 부분에 집중하는 경우&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 계절에만 집중해서 데이터를 형성할 필요가 있습니다. 자연적인 것들, 사람의 행동, 많은 데이터들이 주기성을 갖는 경우가 많습니다. 이런 경우에는 전체 데이터에서 특정 주기 부분만 샘플링 할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;c)&lt;/b&gt;&amp;nbsp;&lt;i&gt;더 낮은 빈도의 데이터에 맞추는 경우&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우는 실제 사용되는 경우로는 훈련 데이터에 비해 테스트 데이터가 너무 작을 경우에 사용하는 방식입니다. 일반적으로는 data imblance가 큰 경우에 사용한다고 합니다. 하지만 단순히 데이터를 삭제하는 건 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;경향성&lt;/b&gt;&lt;/span&gt;을 중요시하는 시계열 데이터의 경향성을 깨트릴 수 있습니다. 그래서 데이터를 취합해서 다운샘플링을 진행해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균이나 합계를 구하는 과정일 수 있지만 나중 값에 더 가중치를 주는 가중치 평균같은 과정을 사용할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 업샘플링 (up sampling)&amp;nbsp;&lt;/b&gt;: 드물게 측정된 데이터를 더 조밀한 시간의 데이터로 형성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름에서는 단순히 다운샘플링과 반대되는 것처럼 보이지만 실제로 단순한 반대개념은 아닙니다. 잘 생각해보면 조밀한 데이터를 줄여내는 과정은 실제로 자주 일어나는 상황입니다. 하지만 업샘플링은 실제로 측정한 것이 아닌 데이터를 얻어내는 과정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R의 시계열 패키지 XTS를 제작한 사람이 했던 말이 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;시계열을 낮은 주기에서 높은 주기로 바꿀 수는 없습니다. 예를 들어 데이터를 주간에서 일간으로, 일간에서 5분 단위로 변환하기 위해서는 마법과도 같은 능력이 필요합니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 높은 빈도로 데이터를 레이블링 해야하는 경우는 필요합니다. 더 많은 레이블이 추가되는 것은 맞지만, 많은 정보가 추가되는 것은 아니라는 것을 알아야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;a)&lt;/b&gt;&amp;nbsp;&lt;i&gt;시계열이 불규칙한 상황&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 시차보다 더 높은 빈도로 전체 데이터를 변환합니다. 이런 경우에는 결측치가 발생할 수 있으므로 결측치를 다루는 방식을 잘 활용해서 데이터를 생성해줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;b)&amp;nbsp;&lt;/b&gt;&lt;i&gt;입력이 서로 다른 빈도로 샘플링된 상황&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 보유한 데이터보다 더 높은 빈도를 요구하는 경우입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 데이터의 시간대를 동일하게 정렬해줘야 합니다. 이때 반드시 사전관찰을 주의해야 합니다. 지금까지 있는 데이터로만 추측을 한다면 안전한 샘플링이 되겠지만 사전관찰이 발생한다면 문제가 발생할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. Smoothing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Smoothing은 '데이터 평활'이라고 합니다. &amp;nbsp;평활이 필요한 이유로는 측정 오류, 너무 높게 튀는 측정치를 제거하는 데에 있습니다. 결측치를 처리할 때, 문제가 있는 데이터가 관여를 한다면 아주 작은 값 하나가 결측치에 영향을 주며 전체 경향을 해칠 수 있기 때문이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시계열에서는 일반적으로 지수평활 (exponential smoothing)을 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지수평활 (exponential smoothin)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간성을 가진 데이터는 최근에 가까울수록 데이터의 가치가 높을 것이라 생각할 수 있습니다. 그리고 실제로도 영향이 있을 수도 있습니다. 그래서 이를 적용하고자 시간상 가까운 데이터에 가중치를 부여하여 시간적 특성을 살려주는 평활 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 t일때 평활된 값을 $S_{t}$라고 할때, $S_{t}$의 식은 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;S_{t} = d \times S_{t-1} + (1-d) \times x_{t} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 식에 따르면 $S_{t-1} = d \times (d \times S{t-2} + (1-d) \times x_{t-1}) + (1-d) \times x_{t}$로 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 토대로 $S_{t}$를 다시 정의하면 아래와 같이 정의됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$S_{t} = d \times (d \times S_{t-2} + (1-d) \times x_{t-1}) + (1-d) \times x_{t} \\ = ( .... ) + d^{n} \times x_{t-n} + d^{n-1} \times x_{t-(n-1)} + ... + d \times x_{t-1}$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식을 보면 알 수 있듯이 d가 0 ~ 1사이 값이하면 오래된 데이터일수록 0에 가까운 값이 곱해지면 해당 데이터가 영향을 줄 확률은 낮아집니다. 이때 d는 가중치라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 평활 요인인 $\alpha$가 존재합니다. $\alpha$가 클수록 현재 값에 가깝도록 더 빨리 갱신됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;422&quot; data-filename=&quot;smothing.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8U4l0/btraL205edc/JCED1c1ujOS8YEISeQ8wfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8U4l0/btraL205edc/JCED1c1ujOS8YEISeQ8wfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8U4l0/btraL205edc/JCED1c1ujOS8YEISeQ8wfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8U4l0%2FbtraL205edc%2FJCED1c1ujOS8YEISeQ8wfk%2Fimg.png&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;422&quot; data-filename=&quot;smothing.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 지수평활에도 단점은 존재합니다. 장기적인 추세의 데이터는 단순한 지수평활 예측을 잘하지 못합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우에는 홀트의 방법이나 홀트-윈터스의 평활을 활용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지수쳥활만이 아닌 다른 평활 방법들도 있습니다.&amp;nbsp;&lt;b&gt;칼만필터 (Kalman Filter)&lt;/b&gt;, &lt;b&gt;뢰스(LOESS, locally estimated scatterplot smoothing) 가&lt;/b&gt;&amp;nbsp;있습니다. 칼만필터는 변동성이나 측정 오차 조합으로 데이터 평활을 합니다. 그리고 뢰스는 지역적으로 데이터를 평활하는 비모수적 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Seasonal Data&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Seasonal data (계절성 데이터)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 행동의 빈도가 안정적으로 반복해서 나타나는 데이터를 계정성 데이터라고 합니다. 대체적으로 인간의 행동은 일일, 주간, 연간의 계절적 변화 경향성을 가집니다. 이를 분석할때, 산점도와 선 그래프를 활용하는데 두 방식에는 각자의 장점이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;457&quot; data-filename=&quot;seasonal.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0qyKf/btraMpWiKdt/K6kedzd998rMWB38hnUFi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0qyKf/btraMpWiKdt/K6kedzd998rMWB38hnUFi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0qyKf/btraMpWiKdt/K6kedzd998rMWB38hnUFi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0qyKf%2FbtraMpWiKdt%2FK6kedzd998rMWB38hnUFi1%2Fimg.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;457&quot; data-filename=&quot;seasonal.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측의 산점도는 원뿔형태의 데이터 분포가 바깥으로 향하게 보여줍니다. 변동성의 추세를 알기는 쉽지 않습니다. 하지만 편차, 분산이 증가한다는 것을 명확하게 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우측의 선그래프는 계절적 변동의 추세를 확인하기 좋습니다. 배수적인 계절성의 변동 크기를 알 수 있다는 장점이 있습니다. 쉽게 말하면 1952년의 계절성 폭보다 1960년의 계절성 폭이 좀 더 배로 증가한 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;569&quot; data-filename=&quot;seasonal decompose.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/widqZ/btraFD9R5zr/4Grv5FLksahCLqtArfc3m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/widqZ/btraFD9R5zr/4Grv5FLksahCLqtArfc3m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/widqZ/btraFD9R5zr/4Grv5FLksahCLqtArfc3m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwidqZ%2FbtraFD9R5zr%2F4Grv5FLksahCLqtArfc3m1%2Fimg.png&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;569&quot; data-filename=&quot;seasonal decompose.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 실제 분포, 추세(경향성), 계절성, 잔차를 한번에 보여주는 방법이 있습니다. 파이썬에는 statsmodel 라이브러리에 있는 &lt;code&gt;seasonal_decompose&lt;/code&gt; 모듈이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627491123387&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from statsmodels.tsa.seasonal import seasonal_decompose

tmp = air.set_index('Date')
ts = tmp.Passengers
result = seasonal_decompose(ts, model='additive')


plt.rcParams['figure.figsize'] = [12, 8]
result.plot()
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃허브 코드 - &lt;a href=&quot;https://github.com/cow-coding/ML-DL-Study/tree/master/Practical%20Time%20Series%20Analysis/Only%20Python&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/cow-coding/ML-DL-Study/tree/master/Practical%20Time%20Series%20Analysis/Only%20Python&lt;/a&gt;&lt;/p&gt;</description>
      <category>Machine Learning/Time Series (시계열)</category>
      <category>time series</category>
      <category>곚ㄹ성</category>
      <category>데이터분석</category>
      <category>시계열</category>
      <category>시계열 분석</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/59</guid>
      <comments>https://cow-coding.tistory.com/59#entry59comment</comments>
      <pubDate>Thu, 29 Jul 2021 01:54:49 +0900</pubDate>
    </item>
    <item>
      <title>[Project] 공공데이터 활용 선거 및 국회의원 정보 제공 서비스</title>
      <link>https://cow-coding.tistory.com/58</link>
      <description>&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;시작하며&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3학년 1학기에 수강하는 데이터베이스 과목의 최종 프로젝트는 &lt;a href=&quot;http://data.go.kr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공공데이터 포털&lt;/a&gt;의 공공 데이터를 활용하여 서비스를 제작하는 것이었습니다. 한학기 동안 진행되는 3개월 프로젝트였으며 저는 제 동기 한명과 팀을 이뤄서 서비스 제작을 했습니다. 프로젝트를 구상하던 당시에 서울특별시장 선거를 포함한 보궐선거가 진행되던 시기라 선거정보 및 국회의원 정보를 제공하는 서비스를 제작하기로 했습니다. 공공데이터 포털에 중앙선거관리위원회의 선거 정보들과 국회 사무처 제공의 국회의원 정보 데이터를 활용했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스는 100% 웹 어플리케이션으로 기획하였으며 기반 백엔드언어는 Node.js를 활용하였습니다. 실제 임시 배포를 하고자 GCP를 사용하였고 서버를 기반으로 하는 GCP OS는 우분투로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 맡은 부분은 데이터베이스 구축과 이를 위한 데이터 파싱작업과 백엔드 API 제작이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;디렉토리 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627375400760&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;├── README.md
├── node_modules
│   └── ...
│   ...
│   └── ...
├── imgs
│   ├── title1.png
│   └── title2.png
├── models
├── package-lock.json
├── package.json
├── public
│   └── src
│       ├── readmore.min.js
│       ├── search.png
│       ├── sidebar.css
│       ├── sidebar_bgd.png
│       ├── stamp.png
│       ├── style.css
│       └── w3_sidebar.css
├── router
│   └── main.js
├── server.js
└── views
    ├── CandInfo.html
    ├── CandProm.html
    ├── Member.html
    ├── PollPlace.html
    ├── PrevElec.html
    ├── about.html
    └── index.html&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데이터 수집 및 파싱&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스는 MySQL을 사용하여 저장했습니다. 이에 따라 테이블 설계를 진행했고 총 6개의 테이블이 설계되었습니다. 각 테이블에 필요한 데이터를 공공데이터 포털의 API를 통해 불러오고 파싱하는 작업을 했습니다. 데이터 파싱과 데이터프레임 제작은 재사용성 향상과 수정의 편의를 위해 모두 모듈화를 통해 제작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 사용 라이브러리&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1627375668378&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
import urllib
import pandas as pd
from urllib.parse import urlencode, quote_plus, unquote, quote
from urllib.request import urlopen
from pandas.io.json import json_normalize
from sqlalchemy import create_engine
import pymysql
import datetime as dt
import sklearn
from sklearn.preprocessing import LabelEncoder
import json
import xmltodict
import multiprocessing as mp
import cryptography

pymysql.install_as_MySQLdb()
import MySQLdb&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 API는 json형식으로 로드하였지만 일부 API는 xml만 한정적으로 로드하기 때문에 xmltodict라이브러리를 추가로 사용했습니다. mysql연결을 위해 sqlalchemy와 pymysql을 활용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 데이터 파싱 및 데이터프레임 제작&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1627375859447&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_election_df(pg_num = 1, num_row = 100):
  election_base_url = 'http://apis.data.go.kr/9760000/CommonCodeService/getCommonSgCodeList'

  page_no = str(pg_num)
  num_of_rows = str(num_row)                                  # maximum 34 datas

  queryParams = '?' + urlencode({
      quote_plus('pageNo') : page_no,
      quote_plus('numOfRows') : num_of_rows,
      quote_plus('resultType') : 'json',
      quote_plus('ServiceKey') : service_key 
  })

  API_election_code_url = election_base_url + unquote(queryParams)

  # Election code load

  '''
  &amp;lt; election code &amp;gt;
  (0)대표선거명               
  (1)대통령,(2)국회의원,(3)시도지사,(4)구시군장,(5)시도의원,(6)구시군의회의원
  (7)국회의원비례대표,(8)광역의원비례대표,(9)기초의원비례대표,(10)교육의원,(11)교육감
  '''

  response = urlopen(API_election_code_url)
  json_str = response.read()

  json_object = json.loads(json_str)

  body = [json_object['getCommonSgCodeList']['item']]

  election_code_data = pd.json_normalize(json_object['getCommonSgCodeList']['item'])
  
  return election_code_data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 데이터 파싱 및 데이터프레임 제작 모듈은 위의 코드를 기반으로 제작되어 있습니다. 기본적인 API를 통해 데이터를 로드하기 전에 필요한 query를 설정하고 서비스키와 함께 요청을 보냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신한 정보의 딕셔너리의 키값 중 'getCommonSgCodeList'의 'item'에서 데이터를 가져옵니다. 그렇게 불러온 데이터를 모두 DataFrame형태로 변환하여 데이터 전처리를 쉽게 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 데이터 전처리&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1627376082301&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def candidate_code_preprocessing():
  election_code = election_code_preprocessing()
  data = election_code.reset_index()  
  election_data = data[['sgId', 'sgTypecode']]

  candidate_df = pd.DataFrame()

  # concat all candidate df
  for i in range(len(election_data)):
    curr_id = election_data['sgId'][i]
    curr_code = election_data['sgTypecode'][i]
    curr_df = pd.DataFrame()

    if curr_code != 0:
      curr_df = get_candidate_df(curr_id, curr_code)
    
    candidate_df = pd.concat([candidate_df, curr_df], ignore_index=True)
  
  # candidate column change
  candidate = candidate_df.drop(['NUM', 'GIHO_SANGSE', 'HANJA_NAME', 'JOB_ID', 'EDU_ID', 'EDU', 'CAREER1', 'CAREER2', 'AGE'], axis=1)
  candidate.columns = ['sgId', 'sgTypecode', 'cnddtId', 'sggName', 'sdName', 'wiwName', 'giho', 'partyName', 'name', 'gender', 'birthday','address', 'job', 'status']

  # encoding data
  le = LabelEncoder()

  for category in ['gender', 'status']:
    candidate[category] = le.fit_transform(candidate[category])

  # giho null value
  candidate[candidate['giho'] == ''] = 1

  # type matching to sql column type
  candidate = candidate.astype({'sgId':int, 'sgTypecode':int, 'cnddtId':int, 'gender':int, 'status':int, 'giho':int})
  candidate['birthday'] = pd.to_datetime(candidate['birthday'], format='%Y-%m-%d')
  candidate.set_index('cnddtId', inplace=True)

  cndd_idx = candidate[candidate['sgId']==1].index
  candidate = candidate.drop(cndd_idx)

  return candidate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스를 설계할 때 필요한 컬럼들을 미리 설정했습니다. 따라서 데이터프레임의 구조를 파악하고 필요한 컬럼을 추출하거나 상황에 따라서는 scikit-learn의 LabelEncoder를 통해 데이터 식별을 했습니다. 사실상 설계와 제작에 가장 오랜시간이 걸린 부분이 데이터 전처리 파트였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서버&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 배포를 위해 GCP (Google Cloud Platform)를 활용했습니다. 모든 API는 Node.js와 express로 제작되었으며 GET방식으로 송수신을 하게 설계하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627376512839&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; app.get(&quot;/CandInfo_do&quot;, function (req, res) {
    var sgId = req.query.date;
    var sggName = req.query.elecplace
    var name = req.query.name;

    if (sgId === undefined) {
      sgId = '';
    }

    var sql = 'SELECT DISTINCT sgId FROM election_code; '
    var sql1 = &quot;SELECT * FROM candidate WHERE sgId LIKE '%&quot;+sgId+&quot;%' AND sggName LIKE '%&quot;+sggName+&quot;%' AND name LIKE '%&quot;+name+&quot;%' ORDER BY giho ASC; &quot;;

    conn.query(sql + sql1, function (err, result) {
      if (err) {
        console.log(err);
      }else {
        var resultArray1 = Object.values(JSON.parse(JSON.stringify(result[0])));
        var resultArray2 = Object.values(JSON.parse(JSON.stringify(result[1])));
        
        res.render(&quot;CandInfo.html&quot;, { codes : resultArray1, candList : resultArray2 , searchSgId:sgId, searchKey: sggName})
      }
    })
  })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지를 이동하는 API가 아닌 유저가 특정 동작을 수행할 경우 반환하는 API입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github - &lt;a href=&quot;https://github.com/cow-coding/V.O.T.E&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/cow-coding/V.O.T.E&lt;/a&gt;&lt;/p&gt;</description>
      <category>Project</category>
      <category>JavaScript</category>
      <category>nodejs</category>
      <category>공공데이터</category>
      <category>선거정보</category>
      <author>DevPolar</author>
      <guid isPermaLink="true">https://cow-coding.tistory.com/58</guid>
      <comments>https://cow-coding.tistory.com/58#entry58comment</comments>
      <pubDate>Tue, 27 Jul 2021 18:04:01 +0900</pubDate>
    </item>
  </channel>
</rss>