<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>wow-kim.log</title>
        <link>https://velog.io/</link>
        <description>def __wow__(?):</description>
        <lastBuildDate>Mon, 08 Mar 2021 10:56:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>wow-kim.log</title>
            <url>https://images.velog.io/images/wow-kim/profile/4777c28e-5449-4b14-856b-28951cfadc12/KakaoTalk_20210121_102931189.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. wow-kim.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/wow-kim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[파이썬 애스터리스크(*)]]></title>
            <link>https://velog.io/@wow-kim/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%95%A0%EC%8A%A4%ED%84%B0%EB%A6%AC%EC%8A%A4%ED%81%AC</link>
            <guid>https://velog.io/@wow-kim/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%95%A0%EC%8A%A4%ED%84%B0%EB%A6%AC%EC%8A%A4%ED%81%AC</guid>
            <pubDate>Mon, 08 Mar 2021 10:56:44 GMT</pubDate>
            <description><![CDATA[<p>Python에서 *(Asterisk)가 어떻게 사용되는지 알아봅니다.</p>
<hr>
<blockquote>
<ol>
<li>곱셈, 거듭제곱</li>
</ol>
</blockquote>
<p>Pass</p>
<blockquote>
<ol start="2">
<li>리스트 확장</li>
</ol>
</blockquote>
<pre><code class="language-python">list_1 = [0]*5 # [0, 0, 0, 0, 0]
tuple_1 = (0,)*5 # (0, 0, 0, 0, 0)</code></pre>
<blockquote>
<ol start="3">
<li>가변인자</li>
</ol>
</blockquote>
<p><strong>가변인자</strong> : 길이가 변할 수 있는 인자(argument), 
함수에 입력이 얼마나 들어오게 될지 모르는 경우에 사용
<code>*args</code> : args라는 tuple로 저장됨
<code>**kwargs</code> : kwargs라는 dict에 저장됨</p>
<pre><code class="language-python">def args_func(*args):
    print(args)

def kwargs_func(**kwargs):
    print(kwargs)

args_func(1, 2) # (1, 2)
kwargs_func(a = 1, b = 2) # {&#39;a&#39;:100, &#39;b&#39;:200}</code></pre>
<blockquote>
<ol start="4">
<li>언패킹(Unpacking)</li>
</ol>
</blockquote>
<p>container 타입의 데이터를 unpacking하는 경우에 사용됨.
(<code>**</code>로 딕셔너리형태에서도 활용가능함.)</p>
<pre><code class="language-python">
from functools import reduce

primes = [2, 3, 5, 7, 11, 13]

def product(*numbers):
    p = reduce(lambda x, y: x * y, numbers)
    return p

product(*primes)
# 30030

product(primes)
# [2, 3, 5, 7, 11, 13]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스]]></title>
            <link>https://velog.io/@wow-kim/%EB%A6%AC%EB%88%85%EC%8A%A4</link>
            <guid>https://velog.io/@wow-kim/%EB%A6%AC%EB%88%85%EC%8A%A4</guid>
            <pubDate>Sat, 06 Mar 2021 23:30:54 GMT</pubDate>
            <description><![CDATA[<h1 id="명령어">명령어</h1>
<ul>
<li><p><code>clear</code></p>
</li>
<li><p><code>ls</code> : 디렉토리 안에 있는 파일 list</p>
<ul>
<li><code>ls -l</code>(우분투 : <code>l</code>) : 좀 더 자세하게(권한, 수정 일자, 용량 등)</li>
<li><code>ls -al</code>(우분투 : <code>ll</code>) : 모든 파일 list(<strong>.으로 시작하는 파일까지 포함</strong>)<ul>
<li><code>a</code>는 보통 <strong>전체</strong>를 의미</li>
</ul>
</li>
<li><code>ls -alt</code> : 시간순으로 정렬</li>
</ul>
</li>
<li><p><code>alias</code> : 단축키(직접 만들 수도 있음)</p>
</li>
<li><p><code>touch</code> : 파일 만들기</p>
</li>
<li><p><code>mv 파일 경로</code> : move(파일을 옮기고 싶다, 이름 바꾸기도 가능)</p>
<ul>
<li><code>.</code> : 현재 경로를 의미</li>
<li><code>..</code> : 이전 경로를 의미</li>
</ul>
</li>
<li><p><code>mkdir</code> : 폴더 만들기(make directory)</p>
</li>
<li><p><code>cd 경로</code> : 폴더 위치 바꾸기(change directory)</p>
<ul>
<li><code>cd</code> : Home 폴더</li>
<li><code>cd -</code> : 이전 폴더로 이동</li>
<li>상대경로 / 절대경로</li>
</ul>
</li>
<li><p><code>cp 파일1 파일2</code> : 파일1을 파일2의 이름으로 복사(copy), 경로를 바꿀수도 있다.</p>
</li>
<li><p><code>rm 파일</code> : 파일 삭제(remove), <strong>휴지통 이런거 없음!</strong>, 폴더는 지울 수 없음!</p>
<ul>
<li><code>rm -i 파일</code> : 지워도 되는지 한번 더 물어봄</li>
<li><code>rm -f 파일</code> : 강제로(force)로 지운다.</li>
<li><code>rm -rf 파일/폴더</code> : 폴더도 지울수 있다.</li>
</ul>
</li>
<li><p><code>top</code> : 내 컴퓨터의 상태를 보여줌(CPU 등), <code>q</code>로 끌 수 있음</p>
</li>
<li><p><code>htop</code> : 좀 더 예쁘게 보여줌</p>
</li>
<li><p><code>ps</code> : 프로세스, 현재 실행하고 있는 프로그램을 볼 수 있다는 것이 의미 있음</p>
</li>
<li><p><code>df</code> : 디스크 정보를 보여줌(램 등)</p>
</li>
<li><p><code>du</code> : 용량을 얼마나 가지고 있는지 보여줌</p>
</li>
<li><p><code>find</code> : 현재 폴더에 들어있는 모든 파일을 검색</p>
<ul>
<li><strong><code>find | grep 파일/폴더</code></strong> : 파일/폴더의 위치를 찾음</li>
</ul>
</li>
<li><p><strong><code>man</code></strong> : 명령어의 기능을 알려줌(manual), 거의 급할때..?</p>
</li>
<li><p><strong><code>which</code></strong> : 명령어의 위치(환경?)를 보여줌</p>
<blockquote>
<p><code>which python3</code></p>
</blockquote>
</li>
<li><p><strong><code>tail</code></strong> : 파일의 변화를 실시간 트래킹, 파일의 끝부분 몇개를 보여줌</p>
</li>
<li><p><code>echo</code> : 입력한거 그대로 출력</p>
</li>
<li><p><code>echo &gt; 파일</code> : 출력에 대한것을 파일에 덮어씀</p>
</li>
<li><p><code>echo &gt;&gt; 파일</code> : 출력에 대한것을 파일에 추가</p>
</li>
<li><p><code>cat 파일</code> : 파일에 입력된 것 출력</p>
</li>
<li><p><strong><code>tail -f 파일</code></strong> : 다른 서비스에서 파일이 변형되는 것을 <strong>모니터링</strong>할 수 있다!</p>
<ul>
<li>빠져나오려면 <code>Ctrl + z</code></li>
</ul>
</li>
<li><p>다중 명령어 세미콜론(;) : <code>명령어;명령어;명령어 + Enter</code></p>
<ul>
<li>중간에 잘못된 명령어가 있어도 끝까지 실행함(<strong>주의!!!</strong>)</li>
</ul>
</li>
<li><p>다중 명령어 &amp;&amp;, ||</p>
<ul>
<li><code>명령어1 &amp;&amp; 명령어2</code> : 명령어 1을 실행하고 문제가 없다면 명령어 2도 실행, 문제있으면 명령어 2 실행 안함</li>
<li><code>명령어1 || 명령어2</code> : 명령어 1을 실행하고 문제가 있다면 명령어 2를 실행, 문제가 없으면 명령어 1만 실행</li>
</ul>
</li>
<li><p>다중 명령어 파이프(|) : 명령어의 결과에 대해 2차적으로 처리하고 싶음.</p>
<ul>
<li><code>grep</code>, <code>awk</code>등과 많이 사용됨.</li>
<li><code>cat 파일 | grep &quot;text&quot; -n</code> : -n은 넘버링, 파일 속에 &quot;text&quot;가 포함된 줄을 출력</li>
<li>로그에서 필요한 것만 정규표현식등으로 가져올 수 있다!!</li>
</ul>
</li>
<li><p><code>vi 파일</code> : 터미널 환경에서 쓸 수 있는 텍스트 편집기, 정규표현식도 가능, </p>
<ul>
<li>Command Mode, Editor Mode, Last Line Mode</li>
</ul>
</li>
<li><p>Redirection(&gt; : 덮어쓰기, &gt;&gt; : 추가) : 표준 출력으로 발생한 결과를 파일로 저장</p>
<ul>
<li><code>ps -ef &gt; ./process list.txt</code></li>
<li><code>echo abc &gt;&gt; 파일</code></li>
<li>어떤 프로그램의 output을 로그 파일로 저장</li>
<li><code>ps -ef | awk &#39; { print $2 }</code></li>
</ul>
</li>
<li><p>File Descriptor : 표준 입출력, 에러를 숫자로 나타낼수 있음</p>
<ul>
<li>0 : stdin : 명령어의 표준 입력<ul>
<li><code>fjgfgkd 1&gt; err_test</code></li>
</ul>
</li>
<li>1 : stdout : 명령어의 표준 출력<ul>
<li><code>dfajfkf 2&gt; err_test</code></li>
</ul>
</li>
<li>2 : stderr : 명령어의 표준 오류</li>
</ul>
</li>
</ul>
<h1 id="tool">Tool</h1>
<ul>
<li>apt-get : 필요한 패키지, 프로그램을 설치 할 수 있는 패키지 관리 도구</li>
<li>htop : top을 좀 더 예쁘게 꾸며서 보여줌ㅎ</li>
<li>nohup : 내 스크립트가 터미널을 끄더라도 계속 실행시키고 싶을 때 &amp;와 함께 백그라운드로 실행</li>
<li>screen(tmux) : nohup을 백그라운드로 가버린 프로세스는 다시 볼 수 없지만 screen을 띄워서 실행하면, 해당 세션을 다시 복원할 수 있음</li>
</ul>
<h1 id="계정과-권한">계정과 권한</h1>
<ul>
<li><p>drwx
소유자의 권한, 그룹의 권한, 소유자가 아닌 사람의 권한을 구분
root 계정 : 모든 권한을 가진 계정</p>
</li>
<li><p><code>chmod 777</code> : 권한을 각각 다르게 줄 수 있음!</p>
</li>
<li><p>sudo : 시스템의 중요한 부분을 변경하거나 업데이트해야 할 때, Root 계정 접속을 남발하면 귀찮기도 하고, 보안상 좋지 않으니 일시적으로 Root의 권한을 위임 받을 수 있는 방법</p>
<ul>
<li>sudo가 가능한 계정이 있음</li>
<li><code>sudo apt-get install ~~</code> </li>
</ul>
</li>
<li><p>환경변수 <code>$PATH</code> : 프로그램을 실행할 때 자동으로 참조하는 절대경로의 모음, 왼쪽으로 갈수록 우선 순위가 높으며, 발견 즉시 바로 실행 됨</p>
<ul>
<li><code>/bin</code> : 일반사용자, 기본적인 명령어</li>
<li><code>/sbin</code> : 슈퍼유저(root), 시스템 관리를 위한 명령어</li>
<li><code>/usr/bin</code> : 일반사용자, <code>/bin</code>에 있는 명령을 제외한 기본적인 명령어</li>
<li><code>/usr/sbin</code> : 슈퍼유저(root), <code>sbin</code>에 있는 명령을 제외한 시스템 관리를 위한 명령어</li>
<li><code>export</code> 명령어로 환경변수를 추가(일시적)</li>
</ul>
</li>
</ul>
<h1 id="쉘-스크립트">쉘 스크립트</h1>
<ul>
<li>쉘 위에서 실행 가능한 명령어를 조합해서 작업을 자동화 하기 위해 사용</li>
<li>똑같은 작업을 자동으로 해버리자!</li>
<li>셔뱅 : `#! 인터프리터 위치, 쉘 스크립트를 시작할 때 맨 처음에 기입, 스크립트를 해석하는 인터프리터를 지정하는 것<ul>
<li>ex) : <code>#!/usr/bin/python3</code></li>
<li>인터프리터 : 쉘 명령어를 해석하는 것</li>
</ul>
</li>
</ul>
<h2 id="변수">변수</h2>
<ul>
<li>선언 : var = ?</li>
<li>사용 : $var</li>
<li>특수한 경우 : ${var}</li>
<li>매개변수 : $0, $1.., $*, $@, 스크립트를 제어</li>
<li>산술 덧셈은 추가 처리 필요 : $((1+2)), 입력 매개변수는 기본적으로 스트링</li>
</ul>
<h2 id="조건문-비교-연산자">조건문, 비교 연산자</h2>
<ul>
<li>if then ~ eilf then ~ fi</li>
<li>정수 -eq, -ne, -gt, -lt, -ge, -le</li>
<li>문자열 ==, !=, -z, -n</li>
<li>정수와 문자열의 비교방식이 다름, <strong>괄호를 두개 넣는다거나</strong> 문자열로 인식되도록 변환하는 방법이 있음.</li>
</ul>
<h2 id="반복문">반복문</h2>
<ul>
<li>while 조건 </li>
<li>for 지정된 범위</li>
<li>break, continue, exit으로 제어</li>
</ul>
<h2 id="유의사항">유의사항</h2>
<ol>
<li>space가 필요한 곳은 확실히 넣어줘야 함</li>
<li><strong>중간에 에러가 나더라도 계속 진행</strong></li>
<li>엔터 잘못 쳤다가 전부 날려먹을 수도...ㄷㄷ</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS]]></title>
            <link>https://velog.io/@wow-kim/AWS</link>
            <guid>https://velog.io/@wow-kim/AWS</guid>
            <pubDate>Sat, 06 Mar 2021 01:01:08 GMT</pubDate>
            <description><![CDATA[<h1 id="1-서비스-개요">1. 서비스 개요</h1>
<p>이점</p>
<ol>
<li>민첩성과 즉각적인 탄력성(On-Demand)</li>
<li>비용 절감 효과</li>
<li>개방성 및 유연성(API)</li>
<li>보안</li>
<li>높은 기술 노하우</li>
</ol>
<p>대표 솔루션</p>
<ol>
<li>어플리케이션 호스팅 : 서버 자원 제공</li>
<li>웹 사이트 : 서버 자원 제공(CPU, Memory, Storage 등)</li>
<li>백업 및 스토리지(S3 등)</li>
<li>데이터베이스</li>
<li>엔터프라이즈 IT</li>
</ol>
<h1 id="2-aws-서비스-레이어">2. AWS 서비스 레이어</h1>
<ol>
<li>AWS Global Physical Infrastructure</li>
<li>Compute(EC2) / Storange(S3) / Network(RDS) / Database(VPC) : Low-Level Building Blocks</li>
</ol>
<hr>
<ol start="3">
<li>Paralled Processing(<strong>Elastic MapReduce</strong>) / Transfer / Content Delivery / App Services / Search : High-Level Building Blocks</li>
<li>Auth, Authorization, Federation/ Monitoring / Deployment and Automation : Cross Service Features</li>
<li>Libraries and SDKs / Web Interface / Tools / Command Line : Tools to access services</li>
<li>Application</li>
</ol>
<h1 id="3-aws-책임-분담-모델">3. AWS 책임 분담 모델</h1>
<h2 id="infrastructure-서비스-모델">Infrastructure 서비스 모델</h2>
<ul>
<li>Managed By AWS(EC2, S3, RDS, VPC 등) </li>
<li>Managed By Customers, Apache같은것 설치 가능</li>
</ul>
<h2 id="abstracted-서비스-모델">Abstracted 서비스 모델</h2>
<ul>
<li>해당 플랫폼을 빌려쓰는 느낌</li>
</ul>
<h1 id="4-ec2elastic-compute-cloud">4. EC2(Elastic Compute Cloud)</h1>
<ul>
<li>EC2는 AWS에서 가장 기본이 되는 low-level 빌딩 블럭에 속하는 <strong>컴퓨팅 서비스</strong>이며, 원하는 만큼 가상 서버를 구축하고 보안 및 네트워크 구성과 스토리지 관리가 가능.</li>
<li>OS를 EC2에 연결되어 있는 AMI(Amazon Machine Image)에 올릴 수 있음</li>
<li><strong>InfraStructure 책임 모델</strong></li>
</ul>
<h2 id="구성요소">구성요소</h2>
<ol>
<li>인스턴스 : 가상 컴퓨팅 환경</li>
<li>AMI(아마존 머신이미지), 인스턴스에 필요한 OS와 소프트웨어가 구성된 템플릿(골드 이미지)</li>
<li>인스턴스 타입 : 가상 서버의 CPU, 메모리 사이즈 용량</li>
<li>EIP(Elastic IP) : 가상의 컴퓨팅 서버에 할당되는 고정 공인 IP</li>
<li>VPC : 가상의 컴퓨팅 서버가 속하는 독립된 네트워크 블럭</li>
</ol>
<h1 id="5-lambda">5. Lambda</h1>
<ul>
<li>이벤트에 응답하여 코드를 실행하고 자동으로 기본 컴퓨팅 리소스를 관리하는 서버리스 컴퓨팅 서비스</li>
<li>백엔드 서버와 운영 체제 유지 관리, 용량 프로비저닝 및 자동 조정, 코드 및 보안 패치 배포, 코드 모니터링, 로깅 등 모든 컴퓨팅 리소스 관리를 수행한ㄷ.</li>
<li><strong>Abstracted 책임 모델</strong></li>
</ul>
<p>New Data -&gt; Batch Layer / Serving Layer / Speed Layer -&gt; &#39;Merge&#39; -&gt; Query</p>
<h1 id="6-컴퓨트-서비스-장애에-대한-디자인">6. 컴퓨트 서비스 장애에 대한 디자인</h1>
<ol>
<li>확장성 : AZ(가용영역) 단위로 확장 가능하며, Autoscailing을 통해 요구되는 트래픽들을 수용한다.</li>
<li>모니터링 및 운영 관리 : API와 대시보드를 통해 손쉽게 관리</li>
<li>이중화 : 여러 AZ에 DB를 구성하여 단일요소의 장애 제거, 하나의 AZ에 장애가 났을 때 서비스를 지속할 수 있도록</li>
<li>Failover : EIP와 Disk를 별도로 관리 가능하여 정상적인 상태의 서버로 대체</li>
</ol>
<h1 id="7-aws-데이터베이스-서비스">7. AWS 데이터베이스 서비스</h1>
<ul>
<li>데이터베이스의 용량과 성능에 맞게 조정 가능 -&gt; 시간 소모적인 관리 작업들(패치, 백업, 확장)등으로부터 자유롭게 해줌</li>
<li>관계형 데이터베이스 : <code>RDS</code> : Mysql, MsSQL, Oracle, PostgreSql 등 사용 가능</li>
<li>비관계형 데이터베이스 : <code>DynamoDB</code> : key-value 형식</li>
</ul>
<h2 id="관계형-데이터베이스">관계형 데이터베이스</h2>
<ul>
<li>키 값에 의해 서로 관련되는 테이블로 구성하는 가장 일반적인 데이터베이스</li>
<li>각 테이블/관계는 하나의 엔티티 타입(고객이나 제품)을 대표</li>
<li>효율적이고 정확하게 운용되기 위해서 ACID 트랜잭션 특징을 가짐
<code>ACID 트랜잭션</code> : 데이터는 학상 원자성, 일관성, 지속성, 고립성을 가져야 함</li>
</ul>
<h2 id="nosql">NoSQL</h2>
<ul>
<li>스키마가 없으며 저장하고 싶은 값을 무제한으로 저장하는 DB </li>
<li>각 데이터 항목으로 고유하게 식별하는 기본 키는 있어야 하지만 다른 속성값에 대해서는 제한이 없음</li>
<li><code>DynamoDB</code>는 JSON 문서를 비롯한 정형 또는 반정형 데이터를 관리할 수 있으며 BASE의 특징을 가짐
<code>BASE</code> : Basically Available, Soft-State, Eventually Consistency(ACID에 비해서는 조금 느슨함)</li>
</ul>
<h1 id="데이터베이스-장애에-대한-디자인">데이터베이스 장애에 대한 디자인</h1>
<ol>
<li>확장성 : AZ 단위로 확장 가능, 다른 AZ로의 두번째 구성 가능</li>
<li>백업 및 운영 관리 : API와 대시보드를 통해 손쉽게 관리</li>
<li>이중화 : 여러 AZ에 DB를 구성하여 단일요소의 장애 제거</li>
<li>Failover : 데이터가 소결합으로 저장되어 있어 대체작동 가능</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[클라우드 컨셉]]></title>
            <link>https://velog.io/@wow-kim/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%BB%A8%EC%85%89</link>
            <guid>https://velog.io/@wow-kim/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%BB%A8%EC%85%89</guid>
            <pubDate>Sat, 06 Mar 2021 00:10:08 GMT</pubDate>
            <description><![CDATA[<h1 id="1-확장성-있는-설계">1. 확장성 있는 설계</h1>
<h2 id="확장성-있는-어플리케이션">확장성 있는 어플리케이션</h2>
<ol>
<li>리소스 증가에 비례한 성능의 증가</li>
<li>회복력(확장성과 일맥상통)<ul>
<li>얼마나 탄력적으로 서비스 트래픽에 따라 자원을 줄이고 늘일 수 있는지</li>
</ul>
</li>
<li>분산된 시스템의 관리 능력</li>
<li>효율적인 운영 방법</li>
<li>비용의 효율적인 관리</li>
</ol>
<h2 id="scale-up-scale-down수직적인-확장">Scale Up/ Scale Down(수직적인 확장)</h2>
<p><code>2 X 4</code> &lt;&gt; <code>4 X 8</code></p>
<h2 id="scale-out-scale-in수평적인-확장">Scale Out/ Scale in(수평적인 확장)</h2>
<p>병렬적으로 확장(1개의 서버 &lt;&gt; 3개의 서버) </p>
<p>Scale Up/Down을 하게 되면 down time이 생길수 밖에 없으므로 scale out이 권장됨.</p>
<h1 id="2탄력성에-대한-이해">2.탄력성에 대한 이해</h1>
<ul>
<li>탄력성 : 최소한의 마찰로 리소스를 스케일링 가능하게 하는 특징.</li>
<li>어플리케이션 구조에 클라우드 이점을 충분히 적용할 수 있도록 탄력성의 개념을 가지고 설계한다.</li>
<li>클라우드 환경에서 전통적인 방식(품질 저하 &amp; 용량 낭비)과 다르게 필요한 자원들만 활용하도록 간소화한 시스템 설계 및 프로세스의 관점의 변화가 필요</li>
<li>on-demand한 특성(리소스를 서비스 양에 맞게 즉각적으로 변화)</li>
</ul>
<h1 id="3-제약에-대한-극복">3. 제약에 대한 극복</h1>
<ul>
<li>시스템의 요구사항들이 클라우드에서 충분히 제공되는지 고민해야함.</li>
<li>On-Premise 환경과 동일한 하드웨어 혹흔 솔루션을 활용하기 어렵다. 클라우드 환경에서 제공하는 추상화된 자원들을 활용하여 서비스를 딜리버리할 수 있도록 관점의 변화가 필요함.</li>
<li>Redis : 인메모리 데이터베이스, key-value 데이터 저장소</li>
</ul>
<h1 id="4-가상화-시스템-관리자">4. 가상화 시스템 관리자</h1>
<ul>
<li>시스템 관리자는 가상화 시스템 관리자의 역할을 수행해야 한다.</li>
<li>데이터베이스 관리자<ol>
<li>DB 설치 및 운영</li>
<li>규칙적인 DB 관리 작업(백업, 패치, 이중화 구성 등)</li>
</ol>
  <strong>3. DB 인스턴스(가상화 인스턴스) 이미지(MySQL, Postgresql 등) 관리</strong>
  <strong>4. 분산 DB 환경의 관리</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[LightGBM]]></title>
            <link>https://velog.io/@wow-kim/LightGBM</link>
            <guid>https://velog.io/@wow-kim/LightGBM</guid>
            <pubDate>Fri, 05 Mar 2021 10:58:51 GMT</pubDate>
            <description><![CDATA[<h1 id="gbm--gradient-boosting-model">GBM : Gradient Boosting Model</h1>
<p>트리 기반 Gradient Boosting 방식 알고리즘이 모델 성능의 우수성은 인정되지만 학습 시간이 너무 오래 걸림</p>
<ul>
<li>XGBoost : 모델 성능도 좋고 병렬 학습이 가능, 하지만 대용량 데이터에 대해서 학습 시간이 오래걸림</li>
<li><strong>LightGBM</strong> : 모델 학습 시간을 대폭 향상 시키기 위해 개발된 방식(마이크로소프트에서 개발!), 랜덤 포레스트보다는 느림ㅎ</li>
</ul>
<p>학습 시간이 오래걸리면 여러가지 방법을 쓰는 것에 제한이 있음, 캐글같은 데이터경진대회에서 베이스라인으로 많이 쓰임.</p>
<h1 id="lightgbm">LightGBM</h1>
<ul>
<li>level-wise 방식은 균형을 맞추며 트리를 생성해나감</li>
<li>Leaf-wise 방식(균형을 맞추지 않으며 Tree를 생성해나가는 방식)으로 Tree를 생성하면 빠르게 트리를 생성해갈 수 있음</li>
<li>이러한 방식은 Overfitting되기 쉬움. 하지만 LightGBM은 내부 하이퍼 파라미터 등으로 그 문제를 잘 해결해냄</li>
</ul>
<h2 id="주요-특징">주요 특징</h2>
<ol>
<li>히스토그램 기반으로 Feature Binning</li>
<li>GBDT 외에 GOSS, DART 방식 제공</li>
<li>오버피팅을 극복하기 위한 다양한 하이퍼 파라미터</li>
</ol>
<h3 id="1-히스토그램-기반-feature-binning">1. 히스토그램 기반 Feature Binning</h3>
<p>Best split gain을 찾기 위해 모든 leaf node의 데이터들을 대상으로 해야함. 오랜 수행 시간 필요
-&gt; lightGBM : 연속형 feature들을 특정한 개수의 bin으로 할당하여 개별 피처들의 범위를 급격히 줄임!</p>
<h3 id="2-boosting-type">2. Boosting Type</h3>
<ul>
<li>default : gbdt(gradient boosting descision tree)</li>
<li>goss, dart, rf(랜덤포레스트, 배깅(부스팅X))</li>
</ul>
<h3 id="21-gossgradient-based-one-side-sampling">2.1 Goss(Gradient Based One-side Sampling)</h3>
<ul>
<li>Gradient 값이 상대적으로 큰 값에 대해서만 선택적으로 필터링하여 반복적으로 재 학습</li>
<li>Gradient 값이 작을 경우 이미 상당한 수준으로 학습이 진행되었다고 가정함.<h3 id="22-dartdropout-meet-multiple-additive-regressing-trees">2.2 Dart(Dropout meet Multiple Additive Regressing Trees)</h3>
</li>
<li>Iteration을 지속적으로 수행하면서 추가적인 트리가 만들어 질 때 마지막에 만들어 지는 트리들이 일부 데이터 세트 조건만을 만족시키기 위해 만들어지는 경우가 발생. 이런 Tree들에 Drop out 적용</li>
</ul>
<h2 id="3-하이퍼-파라미터">3. 하이퍼 파라미터</h2>
<p>LightGBM : leaf-wise 방식이므로 조절해주지 않으면 depth가 끝없이 깊어져서 오버피팅에 취약해짐.</p>
<ul>
<li><p>가장 중요한 파라미터
<code>max_depth</code> : 최대 depth
<code>num_leaves</code> : leaf node의 개수
<code>num_iterations(n_estimators)</code> : 약한 학습기의 개수
<code>learning_rate</code> : 학습률</p>
</li>
<li><p>과적합 제어
<code>bagging_fraction(subsample)</code> : 데이터를 샘플링하는 비율<strong>(행)</strong>
<code>feature_fraction(colsample_bytree)</code> : 트리 생성에 사용하는 피처<strong>(열)</strong>
<code>lambda_l2</code> : L2 Regularization
<code>lambda_l1</code> : L1 Regularization
<code>max_bin</code> : 연속형 변수들 binning할 때 최대 bin의 개수</p>
</li>
</ul>
<h1 id="하이퍼-파라미터-튜닝">하이퍼 파라미터 튜닝</h1>
<ol>
<li>Grid Search</li>
<li>Random Search</li>
<li><strong>Bayesian Optimization</strong></li>
<li>수동 튜닝</li>
</ol>
<h2 id="issue">Issue</h2>
<p>너무 많은 하이퍼 파라미터
gridsearch : 개별 하이퍼 파라미터들을 grid 형태로 지정하는 것은 한계가 존재(데이터가 크면 답이 없다).</p>
<h2 id="bayesian-opt">Bayesian Opt.</h2>
<p>미지의 함수가 반환하는 값의 최대값을 매우 짧은 반복을 통해 찾아내는 최적화 방식!</p>
<ul>
<li><p>Gaussian Process를 통해 함수의 사후 분포를 생성하고, 이를 기반으로 최적화하려는 함수를 재구성</p>
</li>
<li><p>점차 많은 입력 값을 받아서 수행하면서 사후 분포가 점점 개선되고, 함수 반환 값을 최대되는 입력 파라미터 영역을 보다 확실하게 찾게 됨.</p>
<blockquote>
<p>함수 입력 인자의 범위를 설정 &amp; 함수 선언
함수를 잘 선언하는 것이 중요(loss function이 return 값)</p>
</blockquote>
</li>
<li><p>AutoML !</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ML]추천시스템]]></title>
            <link>https://velog.io/@wow-kim/ML%EC%B6%94%EC%B2%9C%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
            <guid>https://velog.io/@wow-kim/ML%EC%B6%94%EC%B2%9C%EC%8B%9C%EC%8A%A4%ED%85%9C</guid>
            <pubDate>Fri, 29 Jan 2021 14:19:32 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="추천-시스템-방식">추천 시스템 방식</h1>
<ol>
<li>Contents Based Filtering<ul>
<li>컨텐츠의 요소들을 기반으로 개인의 취향을 매칭</li>
</ul>
</li>
<li>Collaborative Filtering<ul>
<li>성향이 비슷한 다른 사람들의 선택을 기반으로 매칭</li>
</ul>
</li>
<li>Hybrid(Contents Based + Collaborative Filtering)</li>
</ol>
<hr>
<h1 id="컨텐츠-기반-필터링">컨텐츠 기반 필터링</h1>
<p>(Contents Based Filtering)</p>
<p>감독, 배우, 영화 설명, 장르 등 영화를 구성하는 다양한 컨텐츠들을 텍스트 기반 문서 유사도로 비교하여 추진함</p>
<ol>
<li>컨텐츠에 대한 텍스트 정보들을 피처 벡터화</li>
<li>코사인 유사도로 컨텐츠별 유사도 계산</li>
<li>컨텐츠 별로 가중 평점을 계산</li>
<li>유사도가 높은 컨텐츠 중에 평점이 좋은 컨텐츠 순으로 추천</li>
</ol>
<h2 id="tmdb-5000-movie-dataset">TMDB 5000 Movie Dataset</h2>
<h3 id="1-컨텐츠에-대한-텍스트-정보들을-피처-벡터화">1. 컨텐츠에 대한 텍스트 정보들을 피처 벡터화</h3>
<blockquote>
<p>🔔<code>ast</code>
<strong>A</strong>bstract <strong>S</strong>yntax <strong>T</strong>rees의 약자인데 말 그대로 문자를 자동으로 parsing해서 파이썬 문법에 맞는 구조로 구조화(<strong>객체화</strong>)해줍니다. Tree형의 자료구조이며, 각각의 노드를 갖게 됩니다.
실제로 1.2 + 3.4의 AST를 뜯어보면 다음과 같습니다.</p>
</blockquote>
<pre><code class="language-python">&gt;&gt; import python
&gt;&gt; ast.dump(ast.parse(&#39;1.2 + 3.4&#39;))
Module(body=[Expr(value=BinOp(left=Num(n=1.2), op=Add(), right=Num(n=3.4)))])</code></pre>
<p>🔔<code>ast.literal_eval</code>
<code>eval</code>함수와 비슷하게 문자그대로(literal) 계산을 실행해주는 함수입니다. eval함수와 다른 점은 파이썬 문법에 맞지 않으면 함수를 실행하지 않는 다는 것입니다. 따라서, 둘 중에 사용을 고민한다면 <code>literal_eval</code>을 사용한 것이 더 <strong>안전</strong>합니다.
그 이유는, eval 함수를 사용한 프로그램을 배포하면 사용자가 의도적으로 틀린 파이썬 문법을 사용하여 <strong>공격</strong>할 수 있기 때문입니다.</p>
<pre><code class="language-python">import pandas as pd
import numpy as np
import warnings; warnings.filterwarnings(&#39;ignore&#39;)

movies =pd.read_csv(&#39;./tmdb_5000_movies.csv&#39;)
movies_df = movies[[&#39;id&#39;,&#39;title&#39;, &#39;genres&#39;, &#39;vote_average&#39;, &#39;vote_count&#39;,
                 &#39;popularity&#39;, &#39;keywords&#39;, &#39;overview&#39;]]

pd.set_option(&#39;max_colwidth&#39;, 100)
movies_df[[&#39;genres&#39;]][:1]</code></pre>
<pre><code>genres
[{&quot;id&quot;: 28, &quot;name&quot;: &quot;Action&quot;}, {&quot;id&quot;: 12, &quot;name&quot;: &quot;Adventure&quot;}, {&quot;id&quot;: 14, &quot;name&quot;: &quot;Fantasy&quot;}, {...    </code></pre><p>텍스트 문서 1차 가공, 파이썬 딕셔너리 변환 후 리스트 형태로 변환</p>
<pre><code class="language-python">from ast import literal_eval

movies_df[&#39;genres&#39;] = movies_df[&#39;genres&#39;].apply(literal_eval)
print(movies_df[&#39;genres&#39;].head(1))

movies_df[&#39;genres&#39;] = movies_df[&#39;genres&#39;].apply(lambda x : [ y[&#39;name&#39;] for y in x])
print(movies_df[[&#39;genres&#39;]][:1])</code></pre>
<pre><code>0    [{&#39;id&#39;: 28, &#39;name&#39;: &#39;Action&#39;}, {&#39;id&#39;: 12, &#39;name&#39;: &#39;Adventure&#39;}, {&#39;id&#39;: 14, &#39;name&#39;: &#39;Fantasy&#39;}, {...
Name: genres, dtype: object

genres
[Action, Adventure, Fantasy, Science Fiction]</code></pre><h3 id="2-코사인-유사도로-컨텐츠별-유사도-계산">2. 코사인 유사도로 컨텐츠별 유사도 계산</h3>
<pre><code class="language-python">from sklearn.feature_extraction.text import CountVectorizer

# CountVectorizer를 적용하기 위해 공백문자로 word 단위가 구분되는 문자열로 변환. 
movies_df[&#39;genres_literal&#39;] = movies_df[&#39;genres&#39;].apply(lambda x : (&#39; &#39;).join(x))
count_vect = CountVectorizer(min_df=0, ngram_range=(1,2))
genre_mat = count_vect.fit_transform(movies_df[&#39;genres_literal&#39;])

from sklearn.metrics.pairwise import cosine_similarity

genre_sim = cosine_similarity(genre_mat, genre_mat)
# 유사도가 높은 영화의 index를 얻음
genre_sim_sorted_ind = genre_sim.argsort()[:, ::-1]
print(genre_sim_sorted_ind[:1])</code></pre>
<p>유사도가 높은 순으로 영화의 index를 추출</p>
<pre><code>[[   0 3494  813 ... 3038 3037 2401]]</code></pre><p>특정 영화와 장르별 유사도가 높은 영화를 반환하는 함수 생성</p>
<pre><code class="language-python">def find_sim_movie(df, sorted_ind, title_name, top_n=10):

    # 인자로 입력된 movies_df DataFrame에서 &#39;title&#39; 컬럼이 입력된 title_name 값인 DataFrame추출
    title_movie = df[df[&#39;title&#39;] == title_name]

    # title_named을 가진 DataFrame의 index 객체를 ndarray로 반환하고 
    # sorted_ind 인자로 입력된 genre_sim_sorted_ind 객체에서 유사도 순으로 top_n 개의 index 추출
    title_index = title_movie.index.values
    similar_indexes = sorted_ind[title_index, :(top_n)]

    # 추출된 top_n index들 출력. top_n index는 2차원 데이터 임. 
    #dataframe에서 index로 사용하기 위해서 1차원 array로 변경
    print(similar_indexes)
    similar_indexes = similar_indexes.reshape(-1)

    return df.iloc[similar_indexes]

similar_movies = find_sim_movie(movies_df, genre_sim_sorted_ind, &#39;The Godfather&#39;,10)
similar_movies[[&#39;title&#39;, &#39;vote_average&#39;]]</code></pre>
<pre><code>    title    vote_average
2731    The Godfather: Part II    8.3
1243    Mean Streets    7.2
3636    Light Sleeper    5.7
1946    The Bad Lieutenant: Port of Call - New Orleans    6.0
2640    Things to Do in Denver When You&#39;re Dead    6.7
4065    Mi America    0.0
1847    GoodFellas    8.2
4217    Kids    6.8
883    Catch Me If You Can    7.7
3866    City of God    8.1</code></pre><h3 id="3-가중-평점weighted-rating-계산">3. 가중 평점(Weighted Rating) 계산</h3>
<blockquote>
<p>$가중 평점(Weighted Rating) = \frac{v}{v+m}R + \frac{m}{v+m}C$</p>
</blockquote>
<ul>
<li>v: 개별 영화에 평점을 투표한 횟수</li>
<li>m: 평점을 부여하기 위한 최소 투표 횟수</li>
<li>R: 개별 영화에 대한 평균 평점.</li>
<li>C: 전체 영화에 대한 평균 평점</li>
</ul>
<pre><code class="language-python">C = movies_df[&#39;vote_average&#39;].mean()
m = movies_df[&#39;vote_count&#39;].quantile(0.6)

def weighted_vote_average(record):
    v = record[&#39;vote_count&#39;]
    R = record[&#39;vote_average&#39;]

    return ( (v/(v+m)) * R ) + ( (m/(m+v)) * C )   

movies_df[&#39;weighted_vote&#39;] = movies_df.apply(weighted_vote_average, axis=1) </code></pre>
<h3 id="4-유사도가-높은-컨텐츠-중-평점이-좋은-컨텐츠-순으로-추천">4. 유사도가 높은 컨텐츠 중, 평점이 좋은 컨텐츠 순으로 추천</h3>
<p>앞의 <code>find_sim_movie</code>함수를 수정
후보군을 더 많이(2배) 뽑고 (가중)평점 순으로 <code>top_n</code>만큼 선택</p>
<pre><code class="language-python">def find_sim_movie(df, sorted_ind, title_name, top_n=10):
    title_movie = df[df[&#39;title&#39;] == title_name]
    title_index = title_movie.index.values

    # top_n의 2배에 해당하는 쟝르 유사성이 높은 index 추출 
    similar_indexes = sorted_ind[title_index, :(top_n*2)]
    similar_indexes = similar_indexes.reshape(-1)
# 기준 영화 index는 제외
    similar_indexes = similar_indexes[similar_indexes != title_index]

    # top_n의 2배에 해당하는 후보군에서 weighted_vote 높은 순으로 top_n 만큼 추출 
    return df.iloc[similar_indexes].sort_values(&#39;weighted_vote&#39;, ascending=False)[:top_n]

similar_movies = find_sim_movie(movies_df, genre_sim_sorted_ind, &#39;The Godfather&#39;,10)
similar_movies[[&#39;title&#39;, &#39;vote_average&#39;, &#39;weighted_vote&#39;]]</code></pre>
<pre><code>    title    vote_average    weighted_vote
2731    The Godfather: Part II    8.3    8.079586
1847    GoodFellas    8.2    7.976937
3866    City of God    8.1    7.759693
1663    Once Upon a Time in America    8.2    7.657811
883    Catch Me If You Can    7.7    7.557097
281    American Gangster    7.4    7.141396
4041    This Is England    7.4    6.739664
1149    American Hustle    6.8    6.717525
1243    Mean Streets    7.2    6.626569
2839    Rounders    6.9    6.530427</code></pre><h1 id="협업-필터링collaborative-filtering">협업 필터링(Collaborative Filtering)</h1>
<blockquote>
<p>취향이 비슷한 친구에게 물어보자!</p>
</blockquote>
<ol>
<li><p>최근접 이웃 기반(Nearst Neighbor), 일반적으로 아이템 기반이 선호됨</p>
<ul>
<li>사용자 기반<ul>
<li>특정 사용자와 비슷한 고객들을 기반으로 비슷한 고객들이 선호하는 다른 상품을 추천</li>
<li>비슷한 상품을 구매해온 고객들은 비슷한 고객으로 간주<blockquote>
<p>당신과 비슷한 고객들이 이 상품도 구매했습니다!</p>
</blockquote>
</li>
</ul>
</li>
<li>아이템 기반<ul>
<li>특정 상품과 유사한 좋은 평가를 받은 다른 비슷한 상품을 추천</li>
<li>사용자들로부터 특정 상품과 비슷한 평가를 받은 상품들은 비슷한 상품으로 간주<blockquote>
<p>이 상품을 선택한 다른 고객들은 다음 상품도 구매했습니다!</p>
</blockquote>
</li>
</ul>
</li>
</ul>
</li>
<li><p>잠재 요인 기반(Latent Factor)</p>
<ul>
<li><strong>행렬 분해</strong> 기반 (ex. SVD)</li>
</ul>
</li>
</ol>
<ul>
<li>User Behavior에만 기반하여 추천</li>
<li>상품, 영화 등 사용자가 아직 평가하지 않은 item에 대한 평가를 예측하는 것이 중요한 문제</li>
<li>사용자 - 아이템 평점 데이터 행렬 형태가 필요<strong>(희소 행렬)</strong><ul>
<li>사용자 기반 : <code>사용자(행) X 아이템(열)</code></li>
<li>아이템 기반 : <code>아이템(행) X 사용자(열)</code>
<img src="https://images.velog.io/images/wow-kim/post/dddb07bb-74d5-4522-9320-837ef3d3357e/%EC%B6%94%EC%B2%9C%ED%94%BC%EB%B4%87.PNG" alt=""></li>
</ul>
</li>
</ul>
<h2 id="아이템-기반-협업-필터링">아이템 기반 협업 필터링</h2>
<ol>
<li>사용자-아이템 행렬 데이터를 아이템-사용자 행렬 데이터로 변환</li>
<li>아이템간의 코사인 유사도로 아이템 유사도 산출</li>
<li>사용자가 관람하지 않은 아이템들 중에서 아이템간 유사도를 반영한 예측 점수 계산</li>
<li>예측 점수가 가장 높은 순으로 아이템 추천</li>
</ol>
<blockquote>
<p><strong>Weighted Rating Sum</strong>
$$\hat{R}<em>{u,i} = \frac{\sum\limits^{N}{S</em>{i,N}}R_{u,N}}{\sum\limits^{N}{|S_{i,N}|}}$$</p>
</blockquote>
<ul>
<li>$\hat{R}_{u,i}$ : 사용자 u, 아이템 i의 개인화된 예측 평점 값</li>
<li>$S_{i,N}$ : 아이템 i와 가장 유사도가 높은 Top-N개 아이템의 유사도 벡터</li>
<li>$R_{u,N}$ : <strong>사용자 u의</strong> 아이템 i와 가장 유사도가 높은 TOP-N개 아이템에 대한 실제 평점 벡터</li>
</ul>
<h2 id="movielens-latest-dataset">MovieLens Latest Dataset</h2>
<h3 id="1-아이템-사용자-행렬-데이터">1. 아이템-사용자 행렬 데이터</h3>
<pre><code class="language-python">import pandas as pd
import numpy as np

movies = pd.read_csv(&#39;./ml-latest-small/movies.csv&#39;)
ratings = pd.read_csv(&#39;./ml-latest-small/ratings.csv&#39;)

#로우레벨 사용자 평점 데이터를 사용자-아이템 평점 행렬로 변환
ratings = ratings[[&#39;userId&#39;, &#39;movieId&#39;, &#39;rating&#39;]]
ratings_matrix = ratings.pivot_table(&#39;rating&#39;, index=&#39;userId&#39;, columns=&#39;movieId&#39;)

# title 컬럼을 얻기 이해 movies 와 조인 수행
rating_movies = pd.merge(ratings, movies, on=&#39;movieId&#39;)

# columns=&#39;title&#39; 로 title 컬럼으로 pivot 수행. 
ratings_matrix = rating_movies.pivot_table(&#39;rating&#39;, index=&#39;userId&#39;, columns=&#39;title&#39;)

# NaN 값을 모두 0 으로 변환
ratings_matrix = ratings_matrix.fillna(0)

ratings_matrix_T = ratings_matrix.transpose()
print(ratings_matrix_T.shape)</code></pre>
<pre><code>(9719, 610)</code></pre><h3 id="2-영화들-간-유사도-산출">2. 영화들 간 유사도 산출</h3>
<pre><code class="language-python">from sklearn.metrics.pairwise import cosine_similarity

item_sim = cosine_similarity(ratings_matrix_T, ratings_matrix_T)

# cosine_similarity() 로 반환된 넘파이 행렬을 영화명을 매핑하여 DataFrame으로 변환
item_sim_df = pd.DataFrame(data=item_sim, index=ratings_matrix.columns,
                          columns=ratings_matrix.columns)

print(item_sim_df.shape)
item_sim_df[&quot;Inception (2010)&quot;].sort_values(ascending=False)[1:6]</code></pre>
<pre><code>(9719, 9719)

title
Dark Knight, The (2008)          0.727263
Inglourious Basterds (2009)      0.646103
Shutter Island (2010)            0.617736
Dark Knight Rises, The (2012)    0.617504
Fight Club (1999)                0.615417
Name: Inception (2010), dtype: float64</code></pre><h3 id="3-아이템-기반-인접-이웃-협업-필터링으로-개인화된-영화-추천가중-평점합-기반">3. 아이템 기반 인접 이웃 협업 필터링으로 개인화된 영화 추천<strong>(가중 평점합 기반)</strong></h3>
<p><img src="https://images.velog.io/images/wow-kim/post/c1c51792-b9fa-4f24-a7cb-20ebb03deed4/%EA%B0%80%EC%A4%91%ED%8F%89%EA%B7%A0%ED%95%A9.PNG" alt=""></p>
<pre><code class="language-python"># Weighted Rating Sum(행렬 연산, 내적)
def predict_rating(ratings_arr, item_sim_arr ):
    ratings_pred = ratings_arr.dot(item_sim_arr)/ np.array([np.abs(item_sim_arr).sum(axis=1)])
    return ratings_pred

ratings_pred = predict_rating(ratings_matrix.values , item_sim_df.values)
ratings_pred_matrix = pd.DataFrame(data=ratings_pred, index= ratings_matrix.index,
                                   columns = ratings_matrix.columns)
print(ratings_pred_matrix.shape)</code></pre>
<pre><code>(610, 9719)</code></pre><p>top-n 유사도를 가진 데이터들에 대해서 예측 평점 계산</p>
<pre><code class="language-python">def predict_rating_topsim(ratings_arr, item_sim_arr, n=20):
    # 사용자-아이템 평점 행렬 크기만큼 0으로 채운 예측 행렬 초기화
    pred = np.zeros(ratings_arr.shape)

    # 사용자-아이템 평점 행렬의 열 크기만큼 Loop 수행. 
    for col in range(ratings_arr.shape[1]):
        # 유사도 행렬에서 유사도가 큰 순으로 n개 데이터 행렬의 index 반환
        top_n_items = [np.argsort(item_sim_arr[:, col])[:-n-1:-1]]
        # 개인화된 예측 평점을 계산
        for row in range(ratings_arr.shape[0]):
            pred[row, col] = item_sim_arr[col, :][top_n_items].dot(ratings_arr[row, :][top_n_items].T) 
            pred[row, col] /= np.sum(np.abs(item_sim_arr[col, :][top_n_items]))        
    return pred
ratings_pred = predict_rating_topsim(ratings_matrix.values , item_sim_df.values, n=20)
user_rating_id = ratings_matrix.loc[9, :]
user_rating_id[ user_rating_id &gt; 0].sort_values(ascending=False)[:10]</code></pre>
<pre><code>title
Adaptation (2002)                                                                 5.0
Austin Powers in Goldmember (2002)                                                5.0
Lord of the Rings: The Fellowship of the Ring, The (2001)                         5.0
Lord of the Rings: The Two Towers, The (2002)                                     5.0
Producers, The (1968)                                                             5.0
Citizen Kane (1941)                                                               5.0
Raiders of the Lost Ark (Indiana Jones and the Raiders of the Lost Ark) (1981)    5.0
Back to the Future (1985)                                                         5.0
Glengarry Glen Ross (1992)                                                        4.0
Sunset Blvd. (a.k.a. Sunset Boulevard) (1950)                                     4.0
Name: 9, dtype: float64</code></pre><p>사용자가 관람하지 않은 영화 중에서 아이템 기반의 인접 이웃 협업 필터링으로 영화 추천</p>
<pre><code class="language-python">def get_unseen_movies(ratings_matrix, userId):
    # userId로 입력받은 사용자의 모든 영화정보 추출하여 Series로 반환함. 
    # 반환된 user_rating 은 영화명(title)을 index로 가지는 Series 객체임. 
    user_rating = ratings_matrix.loc[userId,:]

    # user_rating이 0보다 크면 기존에 관람한 영화임. 대상 index를 추출하여 list 객체로 만듬
    already_seen = user_rating[ user_rating &gt; 0].index.tolist()

    # 모든 영화명을 list 객체로 만듬. 
    movies_list = ratings_matrix.columns.tolist()

    # list comprehension으로 already_seen에 해당하는 movie는 movies_list에서 제외함. 
    unseen_list = [ movie for movie in movies_list if movie not in already_seen]

    return unseen_list</code></pre>
<h3 id="4-사용자가-관람하지-않은-영화들-중-예측-평점이-가장-높은-영화를-추천">4. 사용자가 관람하지 않은 영화들 중 예측 평점이 가장 높은 영화를 추천</h3>
<pre><code class="language-python">def recomm_movie_by_userid(pred_df, userId, unseen_list, top_n=10):
    # 예측 평점 DataFrame에서 사용자id index와 unseen_list로 들어온 영화명 컬럼을 추출하여
    # 가장 예측 평점이 높은 순으로 정렬함. 
    recomm_movies = pred_df.loc[userId, unseen_list].sort_values(ascending=False)[:top_n]
    return recomm_movies

# 사용자가 관람하지 않는 영화명 추출   
unseen_list = get_unseen_movies(ratings_matrix, 9)

# 아이템 기반의 인접 이웃 협업 필터링으로 영화 추천 
recomm_movies = recomm_movie_by_userid(ratings_pred_matrix, 9, unseen_list, top_n=10)

# 평점 데이타를 DataFrame으로 생성. 
recomm_movies = pd.DataFrame(data=recomm_movies.values,index=recomm_movies.index,columns=[&#39;pred_score&#39;])
recomm_movies</code></pre>
<pre><code>    pred_score
title    
Shrek (2001)    0.866202
Spider-Man (2002)    0.857854
Last Samurai, The (2003)    0.817473
Indiana Jones and the Temple of Doom (1984)    0.816626
Matrix Reloaded, The (2003)    0.800990
Harry Potter and the Sorcerer&#39;s Stone (a.k.a. Harry Potter and the Philosopher&#39;s Stone) (2001)    0.765159
Gladiator (2000)    0.740956
Matrix, The (1999)    0.732693
Pirates of the Caribbean: The Curse of the Black Pearl (2003)    0.689591
Lord of the Rings: The Return of the King, The (2003)    0.676711</code></pre><h2 id="잠재-요인-협업-필터링">잠재 요인 협업 필터링</h2>
<ul>
<li>사용자-아이템 평점 행렬 속에 숨어 있는 잠재 요인을 추출해 추천</li>
<li>대규모 다차원 (희소) 행렬을 SVD 같은 행렬 분해를 통해 잠재 요인을 추출하는데, 이 <strong>잠재 요인</strong>을 기반으로 평점 행렬을 재구성하면서 추천을 구현합니다.</li>
<li>잠재 요인이 무엇이라고 정확히 특정짓기는 매우 어렵다.</li>
<li>사용자 레벨의 잠재요인 ~ 아이템 레벨의 잠재요인</li>
<li>목표는 희소 행렬 형태의 사용자-아이템 평점 행렬을 <strong>밀집 행렬</strong> 형태의 <strong>_사용자-잠재요인 행렬</strong>과 <strong>잠재 요인-아이템 행렬</strong>로 분해해서 이를 밀집 형태의 사용자-아이템 평점 행렬을 생성해 추천에 사용하는 것<blockquote>
<p>$$R \cong PQ^T$$ = (사용자-잠재요인 행렬)*(잠재요인 - 아이템 행렬)&#39;</p>
</blockquote>
</li>
</ul>
<p><img src="https://images.velog.io/images/wow-kim/post/be8e1aad-2a92-4ec1-aa4a-f12b51b538e3/%EC%9E%A0%EC%9E%AC%EC%9A%94%EC%9D%B8.PNG" alt=""></p>
<p><strong>Singular Value Decomposition(SVD)</strong></p>
<ul>
<li>Factor의 개수 K는 분석가가 정해주어야함</li>
<li>P의 개별 <strong>행</strong>은 개별 <strong>사용자에 대한 잠재요인</strong>을 반영</li>
<li>Q의 개별 <strong>열</strong>은 개별 <strong>아이템에 대한 잠재요인</strong>을 반영<blockquote>
<p>아직 사용자가 평점을 매개지 않은 아이템 [i, j]에 대한 예측평점
= <strong>(P의 i 행 벡터)*(Q의 j열 벡터)&#39;</strong></p>
</blockquote>
</li>
</ul>
<p><img src="https://images.velog.io/images/wow-kim/post/09dd4adb-abb1-41bd-853a-7d912b776969/SVD.PNG" alt=""></p>
<h3 id="행렬-분해-이슈">행렬 분해 이슈</h3>
<ul>
<li>SVD는 결측값이 없는 행렬에 적용 가능함, 일반적인 SVD방식으로 분해 불가</li>
<li><strong><code>경사하강법</code></strong>을 이용해 P와 Q에 기반한 예측 R값이 실제 R값과 가장 최소의 오차를 가질 수 있도록 비용함수 최적화를 통해 P와 Q 최적화</li>
</ul>
<ol>
<li>P와 Q를 임의의 값을 가진 행렬로 설정</li>
<li>P와 Q를 곱해 예측 R 행렬을 계산하고 실제 R 행렬에 해당하는 오류값 계산</li>
<li>이 오류값이 적절히 최소화할 수 있도록 업데이트<blockquote>
<p>$$Cost = min\sum{(r_{u,i}-p_uq_i^t)^2}+\lambda(\lvert q_i^2\rvert+\lvert p_u^2\rvert)$$
(실제값과 예측값의 오류 최소화) + (과적합 개선을 위한 L2 규제)</p>
</blockquote>
</li>
</ol>
<h3 id="경사하강을-이용한-행렬-분해-예제">경사하강을 이용한 행렬 분해 예제</h3>
<p>원본 행렬 R 및 R을 분해할 P와 Q를 임의의 정규분포를 가진 랜덤값으로 초기화</p>
<pre><code class="language-python">import numpy as np

# 원본 행렬 R 생성, 분해 행렬 P와 Q 초기화, 잠재요인 차원 K는 3 설정. 
R = np.array([[4, np.NaN, np.NaN, 2, np.NaN ],
              [np.NaN, 5, np.NaN, 3, 1 ],
              [np.NaN, np.NaN, 3, 4, 4 ],
              [5, 2, 1, 2, np.NaN ]])

num_users, num_items = R.shape
K=3

# P와 Q 매트릭스의 크기를 지정하고 정규분포를 가진 random한 값으로 입력합니다. 
np.random.seed(1)
P = np.random.normal(scale=1./K, size=(num_users, K))
Q = np.random.normal(scale=1./K, size=(num_items, K))</code></pre>
<p>비용계산 함수를 생성. 분해된 행렬 P와 Q.T를 내적하여 예측 행렬 생성하고 실제 행렬에서 널이 아닌 값의 위치에 있는 값만 예측 행렬의 값과 비교하여 RMSE값을 계산하고 반환</p>
<pre><code class="language-python">from sklearn.metrics import mean_squared_error

def get_rmse(R, P, Q, non_zeros):
    error = 0
    # 두개의 분해된 행렬 P와 Q.T의 내적으로 예측 R 행렬 생성
    full_pred_matrix = np.dot(P, Q.T)

    # 실제 R 행렬에서 널이 아닌 값의 위치 인덱스 추출하여 실제 R 행렬과 예측 행렬의 RMSE 추출
    x_non_zero_ind = [non_zero[0] for non_zero in non_zeros]
    y_non_zero_ind = [non_zero[1] for non_zero in non_zeros]
    R_non_zeros = R[x_non_zero_ind, y_non_zero_ind]
    full_pred_matrix_non_zeros = full_pred_matrix[x_non_zero_ind, y_non_zero_ind]

    mse = mean_squared_error(R_non_zeros, full_pred_matrix_non_zeros)
    rmse = np.sqrt(mse)

    return rmse</code></pre>
<p>경사하강법에 기반하여 P와 Q의 원소들을 업데이트 수행</p>
<pre><code class="language-python"># R &gt; 0 인 행 위치, 열 위치, 값을 non_zeros 리스트에 저장. 
non_zeros = [ (i, j, R[i,j]) for i in range(num_users) for j in range(num_items) if R[i,j] &gt; 0 ]

steps=1000
learning_rate=0.01
r_lambda=0.01

# SGD 기법으로 P와 Q 매트릭스를 계속 업데이트. 
for step in range(steps):
    for i, j, r in non_zeros:
        # 실제 값과 예측 값의 차이인 오류 값 구함
        eij = r - np.dot(P[i, :], Q[j, :].T)
        # Regularization을 반영한 SGD 업데이트 공식 적용
        P[i,:] = P[i,:] + learning_rate*(eij * Q[j, :] - r_lambda*P[i,:])
        Q[j,:] = Q[j,:] + learning_rate*(eij * P[i, :] - r_lambda*Q[j,:])

    rmse = get_rmse(R, P, Q, non_zeros)
    if (step % 250) == 0 :
        print(&quot;### iteration step : &quot;, step,&quot; rmse : &quot;, rmse)</code></pre>
<pre><code>### iteration step :  0  rmse :  3.2388050277987723
### iteration step :  250  rmse :  0.029248328780879088
### iteration step :  500  rmse :  0.01697365788757103
### iteration step :  750  rmse :  0.01657420047570466</code></pre><pre><code class="language-python">pred_matrix = np.dot(P, Q.T)
print(&#39;예측 행렬:\n&#39;, np.round(pred_matrix, 3))</code></pre>
<pre><code>예측 행렬:
 [[3.991 0.897 1.306 2.002 1.663]
 [6.696 4.978 0.979 2.981 1.003]
 [6.677 0.391 2.987 3.977 3.986]
 [4.968 2.005 1.006 2.017 1.14 ]]</code></pre><h1 id="파이썬-추천-패키지-surprise">파이썬 추천 패키지 <code>Surprise</code></h1>
<blockquote>
<p><strong>! 주의할 점</strong>
Input 데이터의 형식이 정해져 있습니다.
<strong>user_id(1열), item_id(2열), rating(3열) 열들이 사용자를 기준으로 한 row레벨의 평점 데이터 세트만 입력 가능(네 번째 열 이후부터는 아에 사용하지않음)</strong>
test() : 사이킷런의 predict()와 같습니다.
predict() : 개별 사용자 한명에 대한 예측(전체X)</p>
</blockquote>
<p>Reader클래스로 파일의 포맷팅 지정하고 Dataset의 load_from_file()을 이용하여 데이터셋 로딩</p>
<pre><code class="language-python">from surprise import accuracy 
from surprise.model_selection import train_test_split
from surprise import Reader

reader = Reader(line_format=&#39;user item rating timestamp&#39;, sep=&#39;,&#39;, rating_scale=(0.5, 5))
data=Dataset.load_from_file(&#39;./ml-latest-small/ratings_noh.csv&#39;,reader=reader)</code></pre>
<p>학습과 테스트 데이터 세트로 분할하고 SVD로 학습후 테스트데이터 평점 예측 후 RMSE평가</p>
<pre><code class="language-python">trainset, testset = train_test_split(data, test_size=.25, random_state=0)

# 수행시마다 동일한 결과 도출을 위해 random_state 설정
# SVD 학습은 TrainSet 클래스를 이용해야 함
algo = SVD(n_factors=50, random_state=0)

# 학습 데이터 세트로 학습 후 테스트 데이터 세트로 평점 예측 후 RMSE 평가
algo.fit(trainset) 
predictions = algo.test( testset )
accuracy.rmse(predictions)

# Cross-Validation
from surprise.model_selection import cross_validate 
cross_validate(algo, data, measures=[&#39;RMSE&#39;, &#39;MAE&#39;], cv=5, verbose=True) 

# GridSearchCV
from surprise.model_selection import GridSearchCV

# 최적화할 파라미터들을 딕셔너리 형태로 지정. 
param_grid = {&#39;n_epochs&#39;: [20, 40, 60], &#39;n_factors&#39;: [50, 100, 200] }

# CV를 3개 폴드 세트로 지정, 성능 평가는 rmse, mse 로 수행 하도록 GridSearchCV 구성
gs = GridSearchCV(SVD, param_grid, measures=[&#39;rmse&#39;, &#39;mae&#39;], cv=3)
gs.fit(data)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 문법 모음]]></title>
            <link>https://velog.io/@wow-kim/SQL-%EB%AC%B8%EB%B2%95-%EB%AA%A8%EC%9D%8C</link>
            <guid>https://velog.io/@wow-kim/SQL-%EB%AC%B8%EB%B2%95-%EB%AA%A8%EC%9D%8C</guid>
            <pubDate>Wed, 27 Jan 2021 15:58:20 GMT</pubDate>
            <description><![CDATA[<p>🔔 SQL 공부하면서 접하는 모든 문법들을 정리할 예정입니다.
🔔 <code>Mysql</code> 기준</p>
<hr>
<h1 id="0-데이터베이스-서버">0. 데이터베이스 서버</h1>
<p>데이터베이스 서버 접속
<code>$ mysql -u 사용자명 -p dbname</code>  </p>
<h1 id="1-데이터베이스">1. 데이터베이스</h1>
<blockquote>
<p>CREATE DATABASE <u>database</u></p>
</blockquote>
<ul>
<li>데이터베이스 생성</li>
</ul>
<blockquote>
<p>DROP DATABASE <u>database</u></p>
</blockquote>
<ul>
<li>데이터베이스 삭제</li>
</ul>
<blockquote>
<p>USE <u>database</u></p>
</blockquote>
<ul>
<li>데이터베이스 선택</li>
</ul>
<blockquote>
<p>SHOW DATABASES</p>
</blockquote>
<ul>
<li>데이터베이스 목록 조회</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[한계 돌파!]]></title>
            <link>https://velog.io/@wow-kim/%ED%95%9C%EA%B3%84-%EB%8F%8C%ED%8C%8C</link>
            <guid>https://velog.io/@wow-kim/%ED%95%9C%EA%B3%84-%EB%8F%8C%ED%8C%8C</guid>
            <pubDate>Thu, 21 Jan 2021 13:28:59 GMT</pubDate>
            <description><![CDATA[<p>같이 공부하는 사람들끼리 열품타를 깔았는데
공부하는 시간이 모두에게 보이다보니 평소 이정도면 충분하지하며 멈췄던 것보다 더 열심히 하게되는 것 같다. 
<strong>한계 돌파!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 다이나믹프로그래밍(DP)]]></title>
            <link>https://velog.io/@wow-kim/Python-%EB%8B%A4%EC%9D%B4%EB%82%98%EB%AF%B9%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DDP</link>
            <guid>https://velog.io/@wow-kim/Python-%EB%8B%A4%EC%9D%B4%EB%82%98%EB%AF%B9%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DDP</guid>
            <pubDate>Thu, 21 Jan 2021 12:55:59 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="문제">문제</h2>
<ol>
<li><a href="https://velog.io/@wow-kim">프로그래머스, 3단계, N으로 표현</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스 3단계]N으로 표현]]></title>
            <link>https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84N%EC%9C%BC%EB%A1%9C-%ED%91%9C%ED%98%84</link>
            <guid>https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84N%EC%9C%BC%EB%A1%9C-%ED%91%9C%ED%98%84</guid>
            <pubDate>Thu, 21 Jan 2021 12:02:59 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="문제-설명">문제 설명</h2>
<p>아래와 같이 5와 사칙연산만으로 12를 표현할 수 있습니다.</p>
<p>12 = 5 + 5 + (5 / 5) + (5 / 5)
12 = 55 / 5 + 5 / 5
12 = (55 + 5) / 5</p>
<p>5를 사용한 횟수는 각각 6,5,4 입니다. 그리고 이중 가장 작은 경우는 4입니다.
이처럼 숫자 N과 number가 주어질 때, N과 사칙연산만 사용해서 표현 할 수 있는 방법 중 N 사용횟수의 최솟값을 return 하도록 solution 함수를 작성하세요.</p>
<h2 id="제한사항">제한사항</h2>
<p>N은 1 이상 9 이하입니다.
number는 1 이상 32,000 이하입니다.
수식에는 괄호와 사칙연산만 가능하며 나누기 연산에서 나머지는 무시합니다.
최솟값이 8보다 크면 -1을 return 합니다.</p>
<h2 id="입출력-예">입출력 예</h2>
<table>
<thead>
<tr>
<th>N</th>
<th>number</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>5</td>
<td>12</td>
<td>4</td>
</tr>
<tr>
<td>2</td>
<td>11</td>
<td>3</td>
</tr>
</tbody></table>
<h2 id="문제-풀이">문제 풀이</h2>
<ul>
<li>이 문제는 상향식 방법(Bottom-up, Tabulation)의 다이나믹 프로그래밍 방법으로 풀이했습니다.</li>
<li>다이나믹 프로그래밍을 사용한 가장 큰 이유는 괄호의 존재때문입니다.</li>
<li>괄호가 없다면 단순히 앞에서부터 사칙연산을 모두 구해가며 number와 일치하는지만 확인하면 쉽게 풀이가 가능합니다.</li>
<li>괄호의 존재때문에 <code>i</code>개의 N으로 이루어진 숫자들의 모음은 <ol>
<li><strong><code>j</code>개의 N으로 이루어진 사칙연산 결과 모음</strong>과 </li>
<li><strong><code>i-j</code>개의 N으로 이루어진 모음</strong>의 조합으로 이루어져 있다고 생각하고 문제에 접근해보겠습니다.(<code>1=&lt;j&lt;4</code>) </li>
</ol>
</li>
</ul>
<ol>
<li><p>우선 <code>N</code>이 <code>number</code>와 같다면 <code>N</code> 하나만 필요하므로 <code>1</code>을 리턴하고 <code>dp</code>는 list를 값으로 가지는 딕셔너리로 지정해둡니다. 
여기서 <code>dp[i]</code>는 i개의 N으로 이루어진 사칙연산의 결과값들의 list입니다.</p>
<pre><code class="language-python"> if N == number:
     return 1
 dp = collections.defaultdict(list)
 dp[1].append(N)</code></pre>
</li>
<li><p><code>33</code>, <code>555</code>같이 N이 사칙연산없이 이어붙어진 형태의 수들을 처리해줍니다.</p>
<pre><code class="language-python"> for i in range(2,9):
     dp[i].append(int(str(N)*i))</code></pre>
</li>
<li><p>이제 <strong>중복된 하위 문제들(Overlapping Subproblems)</strong>을 갖는 DP의 특징을 이용합니다. <code>dp[j]</code>의 요소들과<code>dp[i-j]</code>를 사칙연산한 결과를 순서대로 <code>dp[i]</code>에 넣어줍니다.</p>
<ul>
<li><p>for문을 줄이기 위해 <code>extend</code>와 <code>map, lambda</code>로 각 사친연산의 결과를 한번에 처리합니다.</p>
</li>
<li><p>0으로 나누는 경우 Error가 뜨는데 <code>try</code>,<code>except</code> 처리해줍니다.</p>
</li>
<li><p>매 i번째 탐색마다 연산 결과에 타겟인 <code>number</code>가 존재한다면  <code>i</code>를 리턴합니다.</p>
</li>
<li><p><code>i=8</code>까지 탐색했음에도 일치하는 결과값이 없다면 -1을 리턴합니다.</p>
<pre><code class="language-python">for i in range(2,9):
  dp[i].append(int(str(N)*i))
  for j in range(1,5):
      # dp[i]는 dp[i-j]와 dp[j]의 조합
      for d_i in dp[i-j]:
          dp[i].extend(list(map(lambda x:x+d_i, dp[j])))
          dp[i].extend(list(map(lambda x:x-d_i, dp[j])))
          dp[i].extend(list(map(lambda x:x*d_i, dp[j])))
          try:
              dp[i].extend(list(map(lambda x:x//d_i, dp[j])))
          except:
              continue
  if number in dp[i]:
      return i
return -1</code></pre>
<h2 id="최종-제출-코드">최종 제출 코드</h2>
<pre><code class="language-python">import collections
def solution(N, number):
if N == number:
  return 1
dp = collections.defaultdict(list)
dp[1].append(N)

for i in range(2,9):
  dp[i].append(int(str(N)*i))
  for j in range(1,5):
      # dp[i]는 dp[i-j]와 dp[j]의 조합
      for d_i in dp[i-j]:
          dp[i].extend(list(map(lambda x:x+d_i, dp[j])))
          dp[i].extend(list(map(lambda x:x-d_i, dp[j])))
          dp[i].extend(list(map(lambda x:x*d_i, dp[j])))
          try:
              dp[i].extend(list(map(lambda x:x//d_i, dp[j])))
          except:
              continue
  if number in dp[i]:
      return i

return -1</code></pre>
</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스 3단계]네트워크]]></title>
            <link>https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Thu, 21 Jan 2021 08:07:26 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="최종-제출-코드">최종 제출 코드</h2>
<pre><code class="language-python">def solution(n, computers):
    answer = 0
    bfs = []
    visited = [0]*n

    while 0 in visited:
        x = visited.index(0)
        bfs.append(x)
        visited[x] = 1

        while bfs:
            node = bfs.pop(0)
            visited[node] = 1
            for i in range(n):
                if visited[i] == 0 and computers[node][i] == 1:
                    bfs.append(i)
                    visited[i] = 1
        answer += 1
    return answer</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[python] 깊이/너비 우선 탐색(DFS/BFS)]]></title>
            <link>https://velog.io/@wow-kim/%EA%B9%8A%EC%9D%B4%EB%84%88%EB%B9%84-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89DFSBFS</link>
            <guid>https://velog.io/@wow-kim/%EA%B9%8A%EC%9D%B4%EB%84%88%EB%B9%84-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89DFSBFS</guid>
            <pubDate>Thu, 21 Jan 2021 06:26:44 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="문제">문제</h2>
<h3 id="dfs">DFS</h3>
<ol>
<li><a href="https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EC%9D%B4%EC%A4%91%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84%ED%81%90">프로그래머스, 3단계, 여행경로</a><h3 id="bfs">BFS</h3>
</li>
<li><a href="https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC">프로그래머스, 3단계, 네트워크</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스 3단계]여행경로]]></title>
            <link>https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EC%97%AC%ED%96%89%EA%B2%BD%EB%A1%9C</link>
            <guid>https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EC%97%AC%ED%96%89%EA%B2%BD%EB%A1%9C</guid>
            <pubDate>Thu, 21 Jan 2021 06:25:34 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="문제-설명">문제 설명</h2>
<p>주어진 항공권을 모두 이용하여 여행경로를 짜려고 합니다. 항상 ICN 공항에서 출발합니다.</p>
<p>항공권 정보가 담긴 2차원 배열 tickets가 매개변수로 주어질 때, 방문하는 공항 경로를 배열에 담아 return 하도록 solution 함수를 작성해주세요.</p>
<h2 id="제한사항">제한사항</h2>
<p>모든 공항은 알파벳 대문자 3글자로 이루어집니다.
주어진 공항 수는 3개 이상 10,000개 이하입니다.
tickets의 각 행 [a, b]는 a 공항에서 b 공항으로 가는 항공권이 있다는 의미입니다.
주어진 항공권은 모두 사용해야 합니다.
만일 가능한 경로가 2개 이상일 경우 알파벳 순서가 앞서는 경로를 return 합니다.
모든 도시를 방문할 수 없는 경우는 주어지지 않습니다.</p>
<h2 id="입출력">입출력</h2>
<table>
<thead>
<tr>
<th>tickets</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[[ICN, JFK], [HND, IAD], [JFK, HND]]</td>
<td>[ICN, JFK, HND, IAD]</td>
</tr>
<tr>
<td>[[ICN, SFO], [ICN, ATL], [SFO, ATL], [ATL, ICN], [ATL,SFO]]</td>
<td>[ICN, ATL, ICN, SFO, ATL, SFO]</td>
</tr>
</tbody></table>
<h2 id="문제풀이">문제풀이</h2>
<ul>
<li><code>collcetion</code> 내장 모듈에는 <code>defaultdict</code>라는 함수가 있습니다. 이 문제의 경우 <code>list</code>로 입력 형식을 지정해주면 별다른 추가 조치없이 append 명령어를 쓸 수 있습니다.</li>
<li>여행 일정을 그래프로 구성해 DFS 방식으로 풀이했습니다.</li>
</ul>
<ol>
<li>우선 그래프를 구성해줍니다. 알파벳이 앞서는 경로부터 방문해야하기 때문에 <code>sorted</code>해줍니다.<pre><code class="language-python">graph = collections.defaultdict(list)
for a , b in sorted(tickets):
 graph[a].append(b)</code></pre>
</li>
<li><code>dfs</code>함수를 만들어줄겁니다. 알파벳 순으로 정렬했기 때문에 맨 앞의 공항부터 <strong>재귀</strong>적으로 <code>pop(0)</code>을 사용해 추출해서 <code>path</code>에 추가해줍니다. 모든 티켓이 <code>graph</code>에서 추출될때까지 while문이 유지되며 <strong>마지막으로 방문한 도시부터 path에 차곡차곡 쌓이게 됩니다.</strong>  모든 도시를 방문할 수 있도록 문제가 주어졌기 때문에 중간에 끊기는 경우는 고려하지 않습니다.<pre><code class="language-python">path = []
def dfs(a):
 while graph[a]:
     dfs(graph[a].pop(0))
 path.append(a)
</code></pre>
</li>
</ol>
<p>dfs(&quot;ICN&quot;)</p>
<pre><code>3. 재귀를 사용했기 때문에 path는 마지막 공항부터 차곡차곡 쌓여있습니다. 리스트를 거꾸로 뒤집어 줍니다. `[::-1]`
```python
return path[::-1]</code></pre><h2 id="최종-제출-코드">최종 제출 코드</h2>
<pre><code class="language-python">import collections
def solution(tickets):
    graph = collections.defaultdict(list)

    for a , b in sorted(tickets):
        graph[a].append(b)
    path = []
    def dfs(a):
        while graph[a]:
            dfs(graph[a].pop(0))
        path.append(a)

    dfs(&quot;ICN&quot;)

    return path[::-1]</code></pre>
<h2 id="보완">보완</h2>
<ul>
<li><code>pop(0)</code> 연산은 시간복잡도가 $O(n)$이지만 <code>pop()</code>은 $O(1)$입니다.</li>
<li><strong>Stack</strong>으로 구현해서 <code>pop()</code>을 사용하도록 하면 더 효율적인 코드가 될 것 같습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 힙(Heap)]]></title>
            <link>https://velog.io/@wow-kim/Python-%ED%9E%99Heap</link>
            <guid>https://velog.io/@wow-kim/Python-%ED%9E%99Heap</guid>
            <pubDate>Thu, 21 Jan 2021 05:54:22 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="문제">문제</h2>
<ol>
<li><a href="https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EC%9D%B4%EC%A4%91%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84%ED%81%90">프로그래머스, 3단계, 이중우선순위큐</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스 3단계]이중우선순위큐]]></title>
            <link>https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EC%9D%B4%EC%A4%91%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84%ED%81%90</link>
            <guid>https://velog.io/@wow-kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-3%EB%8B%A8%EA%B3%84%EC%9D%B4%EC%A4%91%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84%ED%81%90</guid>
            <pubDate>Thu, 21 Jan 2021 05:53:49 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="문제-설명">문제 설명</h2>
<ul>
<li>이중 우선순위 큐는 다음 연산을 할 수 있는 자료구조를 말합니다.</li>
</ul>
<table>
<thead>
<tr>
<th>명령어</th>
<th>수신 탑(높이)</th>
</tr>
</thead>
<tbody><tr>
<td>I 숫자</td>
<td>큐에 주어진 숫자를 삽입합니다.</td>
</tr>
<tr>
<td>D 1</td>
<td>큐에서 최댓값을 삭제합니다.</td>
</tr>
<tr>
<td>D -1</td>
<td>큐에서 최솟값을 삭제합니다.</td>
</tr>
<tr>
<td>- 이중 우선순위 큐가 할 연산 operations가 매개변수로 주어질 때, 모든 연산을 처리한 후 큐가 비어있으면 [0,0] 비어있지 않으면 [최댓값, 최솟값]을 return 하도록 solution 함수를 구현해주세요.</td>
<td></td>
</tr>
</tbody></table>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>operations는 길이가 1 이상 1,000,000 이하인 문자열 배열입니다.</li>
<li>operations의 원소는 큐가 수행할 연산을 나타냅니다.</li>
<li>원소는 “명령어 데이터” 형식으로 주어집니다.</li>
<li>최댓값/최솟값을 삭제하는 연산에서 최댓값/최솟값이 둘 이상인 경우, 하나만 삭제합니다.</li>
<li>빈 큐에 데이터를 삭제하라는 연산이 주어질 경우, 해당 연산은 무시합니다.</li>
</ul>
<h2 id="입출력">입출력</h2>
<table>
<thead>
<tr>
<th>operations</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[I 16,D 1]</td>
<td>[0,0]</td>
</tr>
<tr>
<td>[I 7,I 5,I -5,D -1]</td>
<td>[7,5]</td>
</tr>
</tbody></table>
<h2 id="문제풀이">문제풀이</h2>
<ul>
<li><code>heapq</code>모듈을 사용합니다</li>
<li>파이썬의 <code>heapq</code>는 <strong>최소힙</strong>만을 지원합니다. 따라서 최소값은 <code>heappop</code>으로 바로 추출할 수 있지만 최대값은 다른 처리를 해주어야합니다.<br></li>
</ul>
<ol>
<li>입력값인 <code>operations</code> 리스트의 element들을 순서대로 받아 공백을 기준으로 <code>op1</code>, <code>op2</code>로 분리합니다.<pre><code class="language-python">answer = []
for op in operations:
 op1, op2 = op.split(&quot; &quot;)
 ...</code></pre>
</li>
<li><code>&quot;I&quot;</code>을 받으면 <code>op2</code>를 int로 변환해(for 값 비교) <code>answer</code>에 추가해줍니다.<pre><code class="language-python"> if op1 == &quot;I&quot;:
     heapq.heappush(answer, int(op2))</code></pre>
</li>
<li>중요한 부분입니다. <code>heapq</code>에는 <code>nlargest</code>라는 힙에서 상위 n개의 값을 리스트 형식으로 추출합니다. 여기서는 <code>op2==&quot;1&quot;</code>일 때, <code>nlargest</code>의 인자를 <code>1</code>로 하여 <code>answer</code>의 상위 1개, 즉, 최대값을 얻어 제거합니다. 
<code>op2 == &quot;-1&quot;</code>이면, 최소힙의 기능을 이용해 쉽게 <code>answer</code>의 최소값을 제거합니다.</li>
</ol>
<pre><code class="language-python">    elif answer:
        if op2 == &quot;1&quot;:
            answer.pop(answer.index(heapq.nlargest(1,answer)[0]))
        else:
            heapq.heappop(answer)
        ...</code></pre>
<ol start="4">
<li><code>answer</code>에 값이 남아 있다면 최대값, 최소값을 return하고 남아 있지 않으면 <code>[0,0]</code>을 return합니다.<pre><code class="language-python"> if answer:
     return [max(answer), min(answer)]
 else:
     return [0,0]</code></pre>
</li>
</ol>
<h2 id="최종-제출-코드">최종 제출 코드</h2>
<pre><code class="language-python">import heapq
def solution(operations):
    answer = []
    for op in operations:
        op1, op2 = op.split(&quot; &quot;)
        if op1 == &quot;I&quot;:
            heapq.heappush(answer, int(op2))
        elif answer:
            if op2 == &quot;1&quot;:
                answer.pop(answer.index(heapq.nlargest(1,answer)[0]))
            else:
                heapq.heappop(answer)
    if answer:
        return [max(answer), min(answer)]
    else:
        return [0,0]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스 2단계]소수_만들기]]></title>
            <link>https://velog.io/@wow-kim/Pro2%EB%8B%A8%EA%B3%84%EC%86%8C%EC%88%98%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@wow-kim/Pro2%EB%8B%A8%EA%B3%84%EC%86%8C%EC%88%98%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 21 Jan 2021 04:14:42 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="문제-설명">문제 설명</h2>
<p>주어진 숫자 중 3개의 수를 더했을 때 소수가 되는 경우의 개수를 구하려고 합니다. 숫자들이 들어있는 배열 nums가 매개변수로 주어질 때, nums에 있는 숫자들 중 서로 다른 3개를 골라 더했을 때 소수가 되는 경우의 개수를 return 하도록 solution 함수를 완성해주세요.</p>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>nums에 들어있는 숫자의 개수는 3개 이상 50개 이하입니다.</li>
<li>nums의 각 원소는 1 이상 1,000 이하의 자연수이며, 중복된 숫자가 들어있지 않습니다.</li>
</ul>
<h3 id="입력">입력</h3>
<ol>
<li><code>nums : [1,2,3,4]</code></li>
<li><code>nums : [1,2,7,6,4]</code><h3 id="출력">출력</h3>
</li>
<li><code>result : 1</code></li>
<li><code>result : 4</code></li>
</ol>
<h2 id="문제-풀이">문제 풀이</h2>
<ul>
<li>우선 소수는 2를 제외하면 무조건 홀수이기 때문에 세 숫자를 더해서 홀수가 되는 경우만 고려했습니다.</li>
<li>(숫자 셋을 더하면 무조건 2이상이므로 2를 예외처리 하지 않았습니다.)</li>
<li>소수 판별은 내장 함수를 추가해 판별합니다.<pre><code class="language-python">def is_prime(x):
  for i in range(2, int(math.sqrt(x))+1):
      if x % i == 0:
             return False
  return True</code></pre>
</li>
<li>주어진 <code>nums</code>를 홀수 <code>odds</code>와 짝수 <code>even</code>으로 구분합니다. 방법의 개수를 저장할 변수 <code>count</code>도 지정해줍니다.<pre><code class="language-python">odds = [n for n in nums if n%2 != 0]
even = [n for n in nums if n%2 == 0]
count = 0</code></pre>
</li>
<li><strong>(1) 홀수 + 홀수 + 홀수</strong></li>
</ul>
<p><code>itertools</code>의 <code>combinations</code>를 이용하여 홀수 3개로 만들 수 있는 모든 조합을 구해 그 중 숫자 셋을 더해 <strong>소수</strong>가 되는 경우 count를 올립니다.</p>
<pre><code class="language-python">if len(odds) &gt;= 3:
    for three_odd in itertools.combinations(odds, 3):
        if is_prime(sum(three_odd)):
            count += 1</code></pre>
<ul>
<li><strong>(2) 홀수 + 짝수 + 짝수</strong>
<code>even</code>에서 <strong>2개의 짝수</strong>를 뽑아 그 둘의 조합의 합과 <code>odds</code>의 <strong>홀수 1개</strong>와 순차적으로 더해가며 소수를 판별합니다.<pre><code class="language-python">if len(odds) &gt;= 1 and len(even) &gt;= 2:
  for odd in odds:
      for two_even in itertools.combinations(even,2):
          if is_prime(sum(two_even)+odd):
              count += 1</code></pre>
</li>
</ul>
<h2 id="최종-제출-코드">최종 제출 코드</h2>
<pre><code class="language-python">import itertools
import math
def solution(nums):

    def is_prime(x):
        for i in range(2, int(math.sqrt(x))+1):
            if x % i == 0:
                return False
        return True

    odds = [n for n in nums if n%2 != 0]
    even = [n for n in nums if n%2 == 0]

    #홀 홀 홀
    count = 0
    if len(odds) &gt;= 3:
        for three_odd in itertools.combinations(odds, 3):
            if is_prime(sum(three_odd)):
                count += 1
    #홀 짝 짝
    if len(odds) &gt;= 1 and len(even) &gt;= 2:
        for odd in odds:
            for two_even in itertools.combinations(even,2):
                if is_prime(sum(two_even)+odd):
                    count += 1

    return count</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 소수]]></title>
            <link>https://velog.io/@wow-kim/Python%EC%86%8C%EC%88%98-%ED%8C%90%EB%B3%84</link>
            <guid>https://velog.io/@wow-kim/Python%EC%86%8C%EC%88%98-%ED%8C%90%EB%B3%84</guid>
            <pubDate>Thu, 21 Jan 2021 02:48:51 GMT</pubDate>
            <description><![CDATA[<h2 id="소수">소수</h2>
<h4 id="2보다-큰-자연수-중-1과-자기-자신을-제외한-자연수로는-나누어-떨어지지-않는-수">2보다 큰 자연수 중 1과 자기 자신을 제외한 자연수로는 나누어 떨어지지 않는 수</h4>
<pre><code class="language-python">import math
def is_prime_number(x):
    for i in range(2, int(math.sqrt(x))+1): # 2부터 x의 제곱근까지 확인
        if x % i == 0:
            return False
    return True</code></pre>
<h2 id="에라토스테네스의-체">에라토스테네스의 체</h2>
<h4 id="2부터-n까지의-모든-자연수에-대한-소수-판별">2부터 N까지의 모든 자연수에 대한 소수 판별</h4>
<ol>
<li><p>2부터 N까지의 모든 자연수를 나열한다.</p>
</li>
<li><p>남은 수 중에서 아직 처리하지 않은 가장 작은 수 i를 찾는다.</p>
</li>
<li><p>남은 수 중에서 i의 배수를 모두 제거한다.(i는 제거하지 않는다.)</p>
</li>
<li><p>더 이상 반복할 수 없을 때까지 2번과 3번의 과정을 반복한다.</p>
<pre><code class="language-python">def prime_list(n):
 # 에라토스테네스의 체 초기화: n개 요소에 True 설정(소수로 간주)
 sieve = [True] * n

 # n의 최대 약수가 sqrt(n) 이하이므로 i=sqrt(n)까지 검사
 m = int(n ** 0.5)
 for i in range(2, m + 1):
     if sieve[i] == True:           # i가 소수인 경우
         for j in range(i+i, n, i): # i이후 i의 배수들을 False 판정
             sieve[j] = False

 return [i for i in range(2, n) if sieve[i] == True]</code></pre>
<p>아래는 프로그래머스에서 본 풀이인데, 시간은 더 오래걸리지만 간결하고 이해하기 쉽습니다.</p>
<pre><code class="language-python">def solution(n):
 num=set(range(2,n+1))

 for i in range(2,n+1):
     if i in num:
         num-=set(range(2*i,n+1,i))
 return list(num)</code></pre>
</li>
</ol>
<h2 id="문제">문제</h2>
<ol>
<li><a href="https://velog.io/@wow-kim/Pro2%EB%8B%A8%EA%B3%84%EC%86%8C%EC%88%98%EB%A7%8C%EB%93%A4%EA%B8%B0">프로그래머스, 2단계, 소수만들기</a></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 내 코드를 Pythonic하게 만들어 줄 함수들]]></title>
            <link>https://velog.io/@wow-kim/Python%EC%9D%98-%EB%A9%8B%EC%A7%84-%EA%B8%B0%EB%8A%A5%EB%93%A4</link>
            <guid>https://velog.io/@wow-kim/Python%EC%9D%98-%EB%A9%8B%EC%A7%84-%EA%B8%B0%EB%8A%A5%EB%93%A4</guid>
            <pubDate>Thu, 21 Jan 2021 01:35:25 GMT</pubDate>
            <description><![CDATA[<h1 id="python">Python</h1>
<h2 id="list">List</h2>
<h2 id="tuple">Tuple</h2>
<h2 id="dict">Dict</h2>
<h2 id="그외-내장-모듈">그외 내장 모듈</h2>
<p>파이썬의 내장 모듈(ex. <code>collections</code>, <code>heapq</code>)은 C로 작성되어 그 속도가 파이썬보다 훨씬 빠릅니다.</p>
<h3 id="getattr"><code>getattr</code></h3>
<pre><code class="language-python">d = deque
getattr(d, &quot;append&quot;)(1)</code></pre>
<p>위 코드는 <code>d.append(1)</code>과 완벽하게 동일한 코드입니다.
일반적으로는 굳이 귀찮게 <code>getattr</code>을 쓸 필요가 없이 <code>.</code>을 사용하는게 더  낫습니다. 
getattr의 특징은 두 번째 인자를 str 자료형으로 받는다는 것인데 다음 예시를 확인해주세요.</p>
<p>HackerRank, <strong>Collections.deque()</strong>
첫 줄은 후에 나올 명령의 개수입니다.
두번째 줄부터는 어떤 명령을 수행할 지를 나타냅니다.</p>
<pre><code>Input : 

6
append 1
append 2
append 3
appendleft 4
pop
popleft</code></pre><p>다음과 같이 코드를 매우 간소화할 수 있습니다.
getattr을 사용하면서 if문으로 하나하나 수작업해야하는 수고를 덜었습니다.</p>
<pre><code class="language-python">from collections import deque
d = deque()
for _ in range(int(input())):
    cmd, *args = input().split()
    getattr(d, cmd)(*args)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Velog!]]></title>
            <link>https://velog.io/@wow-kim/Velog</link>
            <guid>https://velog.io/@wow-kim/Velog</guid>
            <pubDate>Thu, 21 Jan 2021 01:20:06 GMT</pubDate>
            <description><![CDATA[<h1 id="velog-시작">Velog 시작</h1>
<p>코드와 매일 매일 한 것들을 정리하기 위해 블로그를 찾아봤다.
Github Blog랑 티스토리도 만들어봤지만(아주 오래걸려서..)
코드가 깔끔하게 보이고 내가 다시 찾아보기에 편한 Velog로 정착!
잘 부탁드립니다.</p>
]]></description>
        </item>
    </channel>
</rss>