TigerCow.Door

안녕하세요. 문범우입니다.

이번에 소개해드릴 알고리즘 문제는, 2017년 카카오톡 블라인드테스트 1차 코딩시험에서 나왔던 문제중 난이도가 제일 낮다는 소개된 '비밀지도' 문제입니다.


해당 문제는 프로그래머스를 통해, 아래 주소에서 만나보실 수 있습니다.

https://programmers.co.kr/learn/courses/30/lessons/17681?language=python3


난이도가 가장 낮다고 소개된 만큼, 문제자체도 간단하고 풀이도 어렵지 않습니다.

따라서 해당 문제는 추가적인 설명대신 코드만 첨부해드리도록 하겠습니다.

추가적으로 궁금한 사항이 있으시면 언제든지 댓글 및 카카오톡이나 이메일을 통해서 연락주시면 바로 답변드리도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def solution(n, arr1, arr2):
    answer = []
    decode_arr1 = []
    decode_arr2 = []
    tmp_str = ''
    tmp_answer = ''
    for i in arr1:
        tmp_str = str(bin(i))[2:]
        while(len(tmp_str) < n):
            tmp_str = '0'+tmp_str
        tmp_str = tmp_str.replace('0',' ')
        tmp_str = tmp_str.replace('1','#')
        decode_arr1.append(tmp_str)
    for i in arr2:
        tmp_str = str(bin(i))[2:]
        while(len(tmp_str) < n):
            tmp_str = '0'+tmp_str
        tmp_str = tmp_str.replace('0',' ')
        tmp_str = tmp_str.replace('1','#')
        decode_arr2.append(tmp_str)
    
    for i in range(n):
        for j in range(n):
            if (decode_arr1[i][j] == '#'or (decode_arr2[i][j] == '#'):
                tmp_answer += '#'
            else:
                tmp_answer += ' '
        answer.append(tmp_answer)
        tmp_answer = ''
    return answer
cs


블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc

안녕하세요. 문범우입니다.

요새 많은 기업들이 공채시즌이 다가와서 그런지, 평소보다 알고리즘 문제풀이에 대한 학원이나 온라인강의에 대한 광고가 많아진 것 같네요.


요새보면 대부분의 기업에서 SW인원들은 다른 시험보다 코딩테스트를 중요시하고 있고 많은 사람들이 제일 까다로워 하는 부분인 것 같습니다.


요새 개인적으로 공부하는 기계학습이나, 리액트네이티브때문에 블로그활동을 자주못하고 있는데, 오랜만에 프로그래머스에 들어갔다가 2017년 카카오톡 블라인드테스트 1차 코딩문제를 공개해두었길래 이번주에 하나씩 풀어보려합니다.


처음에는 쉬운문제부터 풀어보려했는데.. 나중에 확인해보니 이번에 소개해드릴 '추석트래픽' 문제가 가장 어려웠다고 하네요.


프로그래머스에서 제공하는 작년 카카오톡 코딩테스트 문제는 아래에서 만나보실수 있으며,

https://programmers.co.kr/learn/challenges


이에 대한 전체적인 해설은 아래에서 만나보실수 있습니다.

http://tech.kakao.com/2017/09/27/kakao-blind-recruitment-round-1/



오늘 소개해드릴 '추석트래픽' 문제의 정답률이 약18%라고 하지만, 개인적인 생각으로는 2017 카카오톡 블라인드테스트 1차 코딩테스트에서 총 5시간이 주어졌기때문에 어려웠다기보단 시간이 부족했다는 이야기가 많았을 듯 합니다.


문제에 대한 전체적인 안내나, 난이도정도등에 대해서는 위에 소개해드린 해설에서 확인해보시길 바랍니다.



1. 추석 트래픽


추석 트래픽 문제에 대한 설명은 따로 진행하지 않겠습니다.

제가 말로 주구장창 설명하는 것보다 직접 문제와 예제를 보시는게 이해가 빠를 것 같아서요 :'(


특별히, 예제3번에서 하나의 그림을 보여주고 있습니다.

x축을 시간으로 두고 각각의 트래픽을 bar형태로 표시해두었죠.

그리고 1초라는 시간범위(구간)를 정해서, 가장 많은 트래픽이 포함되는 구간에서의 트래픽 개수를 찾아내고 있습니다.


해당 그림을 보면서 어디서 많이 낯익다 싶었습니다.

바로, An activity-selection problem 문제입니다.

작년 알고리즘수업을 들으면서 봤던 문제인데, 잘 모르시는 분들은 한번 쯤 찾아보셔도 좋을 듯 합니다.


먼저 저는 입력되는 lines 를 하나씩 가져와서 datetime 객체로 바꾸고 이를 end_datetime으로 두었으며 lines에서 주는 실행시간을 가져와서 실행시간의 초단위 값 processing_s 와, 실행시간의 micro second단위 값 processing_ms 를 만들었습니다.

그리고 이 세개의 값를 이용해서, 트래픽의 시작시간을 구해 datetime객체로 하여 start_datetime으로 두었습니다.


이들을 이용해 같은 트래픽끼리 하나의 리스트로 묶어서, start_end_datetime 리스트에 저장하였고, 추후 answer를 탐색하기 위해 sorted_time 리스트를 만들어 start_datetime과 end_datetime의 모든 요소를 같이 저장하였습니다.

그리고 모든 lines에 대한 처리가 끝나면 sorted_time 리스트는 sort함수를 통해 오름차순으로 정렬합니다.


즉, 예제 1번과 같이 입력이 다음과 같다면,

입력: [
2016-09-15 01:00:04.001 2.0s,
2016-09-15 01:00:07.000 2s
]


start_end_datetime = [[ '2016-09-15 01:00:02.002000', '2016-09-15 01:00:04.001000' ], [ '2016-09-15 01:00:05.001', '2016-09-15 01:00:07.000']]


sorted_time = [ '2016-09-15 01:00:02.002000', '2016-09-15 01:00:04.001000', '2016-09-15 01:00:05.001', '2016-09-15 01:00:07.000']


과 같이 만들어지게 됩니다.


이제 문제에서 원하는 답을 찾을 차례입니다.

여기서 저도 한번 헤매고, 1000 micro second마다 탐색하는 방법으로 시도해봤더니 역시나 시간초과에 걸렸었습니다....


하지만 조금 더 생각해보면, 구하고자 하는 초당 최대 처리량이 변하는 순간은 단지 어떤 트래픽의 시작 또는 종료 시점뿐 입니다.

즉, 위에서 만들어두었던 sorted_time 리스트에 있는 시간에서만 초당 최대 처리량의 변화가 발생합니다.

따라서 우리는 sorted_time 리스트를 범위로 for문을 돌리면 되고, sorted_time 리스트에서 꺼낸 하나의 요소를 compare_time으로 두었고, 여기에 1초를 더한 시간을 compare_time_one으로 두었습니다.

그리고 start_end_datetime에서 하나씩 꺼내어 compare_time과 compare_time_one이라는 범위에 해당 트래픽이 속하여있는지를 탐색하고 각각의 탐색에 따른 최대값을 찾아 정답으로 반환하면 됩니다.


설명이 잘 된지는 모르겠으나, 실제로 코드를 보시면서 이해해보시면 잘 이해할 수 있을 것이라고 생각됩니다.


전체적인 python 코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import datetime
 
def solution(lines):
    start_end_time = []
    sorted_time = []
    tmp_answer = 0
    answer = tmp_answer
    for line in lines:
        split_line = line.split()
        split_day = split_line[0].split('-')
        split_time = split_line[1].split(':')
        split_s = split_time[2].split('.')
 
        Y = int(split_day[0]); M = int(split_day[1]); D = int(split_day[2])
        h = int(split_time[0]); m = int(split_time[1])
        s = int(split_s[0]); ms = int(split_s[1])*1000
        
        end_datetime = datetime.datetime(Y,M,D,h,m,s,ms)
        
        split_processing = split_line[2][:-1].split('.')
        processing_s = int(split_processing[0])
        if len(split_processing) == 1:
            start_datetime = end_datetime - datetime.timedelta(seconds=processing_s)
        else:
            processing_ms = int(split_processing[1]) * 1000
            start_datetime = end_datetime - datetime.timedelta(seconds=processing_s) - datetime.timedelta(microseconds=processing_ms)
        start_datetime = start_datetime + datetime.timedelta(microseconds=1000)
        start_end_time.append([start_datetime,end_datetime])
        sorted_time.append(start_datetime)
        sorted_time.append(end_datetime)
    sorted_time.sort()
    
    for compare_time in sorted_time:
        compare_time_one = compare_time + datetime.timedelta(seconds=1)
        if compare_time >= start_end_time[-1][1]:
            break;
        for each in start_end_time:
            if (compare_time <= each[0])and(each[0< compare_time_one):
                tmp_answer += 1
            elif (compare_time <= each[1])and(each[1< compare_time_one):
                tmp_answer += 1
            elif (each[0<= compare_time)and(compare_time_one <= each[1]):
                tmp_answer += 1
        if answer < tmp_answer:
            answer = tmp_answer
        tmp_answer = 0
    if answer == 0:
        answer += 1
    return answer
cs


만약 코드에 대해 궁금한 사항이나, 보다 효율적인 방법에 대해서 말씀해주실 점이 있다면 언제든지 댓글 또는 카카오톡, 이메일을 이용해서 말씀해주세요 :)

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요. 문범우입니다.


최근 멋쟁이 사자처럼 6기 운영진으로 활동하며, 지난 8월말에 해커톤을 진행하였습니다.

저는 방학간 파이썬, 장고 스터디를 진행하며 함께 공부한 친구들과 장고를 활용한 공유일기장 플랫폼 웹사이트를 개발하였습니다.


aws ec2 프리티어을 이용하여 배포까지 완료하였으나, 실제로 서비스 론칭등의 계획은 없습니다.

장고를 직접 활용해보고 6기 인원들에게는 배포까지 해보는 경험으로써의 토이 프로젝트였습니다.


활용된 스택은 다음과 같습니다.


python: 3.6.5


django: 2.0


postgresql: 10.4



해당 프로젝트에서 저는, 프로젝트 전체 기획 및 진행을 담당하며 세부적으로는 데이터베이스 설계 및 구축, 교환일기장 기능 개발을 담당하였습니다.



- 서비스 소개


카카오톡 소셜 로그인



장고를 활용한 공유일기장 플랫폼에서는 카카오톡 소셜로그인을 통해 누구나 쉽게 서비스를 이용할 수 있도록 개발하였습니다.



메인화면 달력 및 작성한 일기 확인하기


사용자는 메인화면에서 달력을 확인할 수 있고, 그 전에 작성한 일기들은 해당 요일에 제목이 표시됩니다. 제목을 누르면 해당 일기로 이동할 수 있습니다.



일기 작성하기


사용자는 일기 쓰기 탭을 눌러서 일기를 작성할 수 있습니다.

제목, 작성할 교환일기장, 감정, 날씨, 사진, 내용을 입력합니다.

하나라도 작성되지 않을시에는 저장되지 않고 빈칸을 알려줍니다.



교환일기장


교환일기장에서는 개인이 작성한 일기 뿐아니라 다른 사람의 일기를 확인할 수 있습니다. 관심사가 같거나, 친구들끼리 함께 일기를 작성하고 싶을때는 교환일기장을 만들거나, 참여하여 함께 일기를 작성할 수 있습니다.



이 외에도 일기를 보관할 수 있는 '뜯어가기' 기능과 댓글기능 등을 구현하였습니다.



배포된 프로젝트의 모든 코드는 아래 깃헙에서 확인하실 수 있습니다.

https://github.com/doorBW/LAN-Diary


이 외에도 추가적인 질문등은 언제든지 댓글 및 이메일, 카카오톡을 이용해주세요 :)

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요. 문범우입니다.

이번 포스팅부터는 aws 서버에 장고 프로젝트를 배포하는 방법에 대해서 알아보도록 하겠습니다.


먼저 진행되는 환경은 다음과 같습니다.


macOS High Sierra

python version: 3.6.5

django version: 2.0


이번 포스팅에서 진행되는 가상환경을 구성하기 위해 필요한 pyenv, virturalenv 등을 제외하고는 윈도우도 거의 비슷한 진행방식으로 가능할 것이라 생각됩니다.


1. pyenv 설치


장고 프로젝트를 배포할때 먼저 로컬에서 가상환경을 구성하고, 그 위에 배포할 장고 프로젝트를 구성해야 합니다.

또한 aws 서버에서 ubuntu를 이용하며 똑같이 가상환경을 이용합니다.

먼저 pyenv 설치하기 전에 아래와 같은 추가적인 패키지들을 설치해줍니다.


1
2
3
$ sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev
cs


위의 내용들은 서버 배포시에 공통적으로 발생하는 문제들을 막기 위해 설치하는 것들이며 pyenv 사이트에도 나오는 내용입니다.

https://github.com/pyenv/pyenv/wiki/Common-build-problems


이제 git clone을 통해 실제로 pyenv를 설치하고 환경변수 설정까지 진행해 보도록 하겠습니다.


1
2
3
4
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
cs


저와 같은 경우에는 zsh를 사용하기 때문에 위의 명령어 중 아래 3줄에 대해서 ~/.bash_profile 대신 ~/.zshrc 를 입력합니다.


1
2
3
$ source ~/.bash_profile
$ pyenv versions
* system (set by /home/nelp/.pyenv/version)
cs


이제 위의 명령어로 터미널창을 재시작해주고 정상적으로 설치되었는지 확인하기 위해 pyenv versions 명령어를 입력합니다. 현재 우리는 pyenv를 통해 추가적으로 설치한 것이 없기 때문에 단순히 system만 나올 것 입니다.


이제 pyenv를 통해 원하는 파이썬 버전을 설치하고 즉각적으로 이용할 수 있습니다.

특정 버전을 설치하는 방법은 아래와 같습니다.


1
$ pyenv install 3.6.5
cs


위와 같이 pyenv install ~ 뒤에 특정 버전을 입력해주면 설치가 되고, 해당 버전을 사용하기 위해서는


1
$ pyenv shell 3.6.5
cs


와 같이 입력하면 됩니다.



2. virtualenv 설치


먼저 pyenv와 같이 git clone을 이용하여 설치합니다.


1
2
3
$ git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
cs


이를 통해 virtualenv가 설치되었으면 이제 우리는 특정 파이썬 버전을 가지는 가상환경을 구성할 수 있습니다.


가상환경을 만드는 방법은,


1
pyenv virtualenv 3.6.5 myenv
cs


와 같이, pyenv virtualenv <원하는 파이썬 버전> <가상환경 이름> 을 입력해주면 됩니다.

이때 파이썬 버전은 pyenv 를 통해 설치되어 있는 버전만 가능합니다.


현재 생성되어 있는 가상환경 목록을 보기 위해서는 다음의 명령어를 입력하면 됩니다.


1
pyenv virtualenvs
cs


또한, 자신이 만든 가상환경으로 진입하기 위해서는,


1
pyenv activate myenv
cs


와 같이, pyenv activate <가상환경 이름> 을 입력하면 됩니다.


가상환경에 진입하면 터미널에서 유저이름 앞에 (myenv) 와 같이 진입되어 있는 가상환경의 이름을 보여줍니다.


만약 가상환경을 나가고 싶다면,


1
pyenv deactivate
cs


를 입력하시면 됩니다.



3. 서버 배포를 위한 장고 환경 구성하기


위에서 pyenv 와 virtualenv의 설치가 정상적으로 되었다면, 실제로 서버 배포를 위해 장고프로젝트의 가상환경을 구성해보도록 하겠습니다.


먼저 파이썬 3.6.5 버전의 django_deploy라는 가상환경을 만듭니다.


1
pyenv virtualenv 3.6.5 django_deploy
cs


그리고 django_deploy라는 이름의 폴더를 새로 만들고 해당 폴더로 들어갑니다.


1
2
mkdir django_deploy
cd django_deploy
cs


그리고 해당 위치에서 다음과 같은 명령어를 입력합니다.


1
pyenv local django_deploy
cs


위의 명령어는 해당 위치에 .python-version 파일을 만들어주면서, 해당 폴더로 가면 바로 django_deploy라는 가상환경에 진입하도록 해주는 명령어 입니다.



이렇게 장고 프로젝트를 위한 기본적인 가상환경 구성은 완료입니다.

이제 해당 가상환경에서 django를 설치하고, pip freeze > requirements.txt 명령어를 통해 패키지목록을 저장해둡니다.

또한 자유롭게 git 설정을 하시면 됩니다.

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요.

이번 포스팅에서는 django에서 admin 페이지를 사용해보도록 하겠습니다.



1. Admin 계정 만들기


먼저 우리는 지난 포스팅까지해서 django와 postgresql의 django_test라는 데이터베이스를 만들어서 연결하였습니다.

그리고 django에서 post라는 모델을 생성하여 migrate 해줌으로써 실제 django_test라는 데이터베이스 내부에 blog_post라는 테이블이 생성된 것을 확인하였습니다.


django에서는 model에 대한 data를 간편하게 확인하고 조작할 수 있는 admin 페이지를 제공해주는데, 이때 admin페이지에는 아무나 접근 가능한 것이 아니고, 서버개발 과정에서 admin 계정을 직접 만들어주어야 합니다.

admin 계정을 만들기 위해서 manage.py 파일이 있는 위치에서 아래 명령어를 입력해줍니다.


python manage.py createsuperuser



명령어를 입력하면 위와 같이 admin 계정에 대한 정보를 입력할 수 있습니다.

여기서 입력한 Username과 password를 통해 admin 페이지에 접근가능합니다. 



2. Admin 페이지 사용하기


이렇게 admin 계정을 만들었다면 이제 서버를 돌려서 주소의 가장 끝에 /admin 을 붙여 admin 페이지에 접근해보겠습니다.



이렇게 접근하면, 아래와 같은 화면이 나타나게 됩니다.



이제 위에서 만들었던 admin 계정의 username과 password를 입력해서 로그인을 합니다.


그런데 로그인을 하고나서 확인을 해보아도 우리가 이전에 만들었던 post와 관련된 것은 찾아볼 수 없습니다.

우리가 마이그레이션을 통해 만들었던 post 모델을 admin 페이지에서 보기 위해서는 이를 admin 페이지에 등록을 해주어야 합니다.


우리가 만든 blog라는 app의 폴더로 들어가보면 admin.py 라는 파일이 있습니다. 해당 파일을 켜서 아래와 같이 작성해줍니다.


1
2
3
4
5
from django.contrib import admin
from .models import Post
# Register your models here.
 
admin.site.register(Post)
cs


그리고 해당 파일을 저장한 뒤에 다시 admin 페이지를 새로고침 해보면,



위와 같이 blog 라는 app에 Posts 라는 것이 새로 생긴것을 확인할 수 있습니다.

실제로 저것을 통해 Posts 라는 테이블에 저장된 값들을 확인할 수 있으며 우리가 직접 데이터를 추가할 수도 있습니다.


이를 직접 확인해보기 위해서, admin 페이지에서 데이터를 입력해보고 postgresql에서 확인해보도록 하겠습니다.


admin 사이트에서 Posts 우측에 있는 Add 버튼을 눌러서 데이터를 추가해보겠습니다.



위와 같이 제목과 내용을 입력하고 Save 버튼을 눌러 데이터를 저장합니다.


그럼 실제로 admin 페이지에서 데이터가 성공적으로 입력되었다는 문구가 뜨고, Posts object도 하나 생긴것을 볼 수 있습니다.


이제 postgresql에서 확인해보도록 하겠습니다.



간단한 SQL문장으로 해당 테이블을 확인해보니 정상적으로 데이터가 입력된 것을 볼 수 있습니다.



이렇게 해서 admin 페이지 사용에 대한 간략한 설명을 진행해보았습니다.

추후에 지속될 포스팅 내용으로는, 개발된 django 어플리케이션을 aws를 이용하여 배포하는 방법을 진행해보도록 할 예정입니다.

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc

안녕하세요. 문범우입니다.

오랜만에 파이썬으로 풀이한 재밌는 알고리즘 문제를 가져왔습니다.

알고리즘 문제는 프로그래머스의 알고리즘 연습에 나온 야근 지수 문제이며 해당 문제는 아래 주소에서 풀어볼 수 있습니다.

문제에 대한 설명도 해당 주소에 나와있기에 문제에 대한 설명은 생략하겠습니다.

https://programmers.co.kr/learn/courses/30/lessons/12927


사실 예전에 매우 간단히 풀이한 문제인데

다시 확인해보니 문제 개편이 되면서..

테스트 케이스가 매우 까다롭게 변했더라구요.

그래도 정확도 통과는 비교적 무난했지만, 효율성 테스트에서 계속 막혀 씨름을 하다가 마침내 풀게되었습니다.

코드와 함께 간단한 해설을 첨부합니다.

추가적으로 궁금하신점이 있으신분들은 이메일이나 카카오톡으로 언제든지 문의해주세요 :)


1. 정확도 통과, 효율성 실패


먼저 정확도는 통과했지만 효율성에서 실패한 처음 코드입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def solution(n, works):
    result = 0 # 결과를 담을 변수
    sumOfWorks = 0 # 모든 일의 합을 담을 변수
    for i in works: # 모든 일의 합을 구한다.
        sumOfWorks+=
    # 남은 일보다 n이 클때는 0을 반환
    if(n >= sumOfWorks): return 0
 
    # n이 0이될때 까지 반복
    while(n!=0):
        works[works.index(max(works))]-=1 # works에서 가장큰 값을 하나 줄이면서
        n-=1 # n의 값을 하나 줄인다.
        if(min(works) == max(works)): break
        # 만약 works에서 가장큰 값과 가장 작은 값이 같아지면 반복문을 나간다.
 
    # n이 0까지 줄어들어든 것이 아니라면
    if not n==0:
        # 이때는, works에서 가장큰값과 가장 작은 값이 같은 상황
        # 즉, works의 모든 값이 동일한 상황이다.
        # 따라서, n의 크기만큼의 요소를 -1씩해서 각각을 제곱하여 결과에 더하고
        # 나머지는 -1을 하지 않고 제곱해서 결과에 더한다.
        return n*((min(works)-1)**2+ (len(works)-n)*((min(works))**2)
    # n이 0까지 줄어든 것이라면 남은 works의 모든 값을 제곱해서 더한다.
    else:
        for i in works:
            result += i**2
 
    # 야근 지수를 최소화 하였을 때의 야근 지수는 몇일까요?
    return result
cs


코드에 대한 상세한 내용은 주석으로 설명하였습니다.

전체적인 알고리즘을 말씀드리면,

단순히 works 리스트에서 가장 큰 값을 찾아 이를 하나씩 줄이는 방법입니다.

추가로, 처음에 모든 남은 일의 합이 n 보다 작으면 0을 반환하게 하는 특수조건이 있습니다.

또한, works 리스트에서 값을 하나씩 줄이다가 최소값과 최대값이 동일한 시점, 즉 works 리스트의 모든 요소가 같아지는 시점도 따로 빼내어서 바로 계산하게끔 처리하였습니다.


위와 같은 풀이는 제목과 같이 정확도 테스트 케이스는 모두 통과하였으나 효율성 테스트 케이스에서 통과하지 못했습니다.

이에 따라 조금 다른 방식으로 생각해서 풀어보았습니다.



2. 정확도 통과, 효율성 통과


먼저 두번째로 풀이하여 정확도와 효율성 모두 통과한 코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def solution(n, works):
    result=0 # 결과를 담을 변수
    works.append(0# 최소값을 위해 works에 0추가
    works.sort() # works를 오름차순으로 정렬
    for i in range(1,len(works)): 
        # works에 대해 맨뒤에서 부터 확인할 것임
        # 인덱싱하기 편하게 하도록 i는 1부터 시작
        tmp = works[-i] - works[-(i+1)] # works에서 첫번째로 큰 숫자와 두번째로 큰 숫자의 차이 구함
        if tmp*< n: # 해당 차이 x 몇번째인지가 n보다 작으면
            n -= tmp*# 그만큼 n을 빼주고
            for j in range(1,i+1):
                works[-j] -= tmp # 첫번째로 큰 숫자를 두번째로 큰숫자와 같게 만든다.
        else# 해당 차이 x 몇번째인지가 n보다 작은게 아니라면
            q = n//# n에 대해서 몇번째인지로 나눈다. 이때 몫은 q, 나머지는 n
            n = n%i
            for j in range(1,i+1):
                works[-j] -= q # 제일 뒤의 숫자부터, i번째까지 몫만큼 빼준다.
            for j in range(1,n+1):
                works[-j] -= 1 # 나머지 처리
            break # 끝
    for i in works:
        result += i**2
 
    # 야근 지수를 최소화 하였을 때의 야근 지수는 몇일까요?
    return result
cs


해당 코드또한 주석으로 설명을 달아놓았지만, 적절한 예시가 없으면 바로 이해하기 힘들수 있기에 하나의 예시를 통해 어떤식으로 알고리즘이 result를 찾아내는지 확인해보도록 하겠습니다.


먼저 입력으로 n = 100, works = [10,9000,9997,9998,9999,10000] 이 들어온다고 가정합니다.

works가 위와 같이 정렬된 상태가 아니어도 4번째줄 코드에서 오름차순으로 정렬합니다.

5번째줄의 for문부터 확인해보도록 하겠습니다.

먼저 첫번째 for문은 i가 1부터 (works의 길이)-1 이므로, 해당 예시에서는 1부터 5까지 반복됩니다.

다음 줄을 보시면 아시겠지만 works의 뒤의 요소부터 인덱싱을 할때 -i 라고 명시하기때문에 i값이 1부터 시작되도록 하였습니다.

(works[-1]은 works의 가장 뒤에 있는 요소를 인덱싱합니다.)

따라서 첫번째 반복에서 tmp = 1 이 됩니다.

그리고 9번째줄 if문을 확인하면 tmp = 1 * i = 1 => 1이 되기 때문에 n보다 작아서 10~12번째줄을 수행하게 됩니다.

10번째줄에 의해서 n이 1000에서 999로 줄어들게 되고, works는 다음과 같이 변화합니다.

works = [10,9000,9997,9998,9999,9999]

즉, 뒤에서 첫번째 요소를 두번째 요소와 같게 만듭니다.


그리고 i가 하나 증가하고 첫번째 for문의 두번째 반복이 진행됩니다.

같은 방법에 의해서, n은 999에서 997로 줄어들게 되고, works는 다음과 같이 변화합니다.

works = [10,9000,9997,9998,9998,9998]

첫번째 반복에서는 n이 1만큼 감소했지만 두번째 반복에서는 i가 2이기 때문에 n이 2만큼 감소합니다. 즉, 뒤에서 첫번째, 두번째 요소를 세번째요소와 같게 만드는 것입니다.


세번째 반복에서도 동일하여, n은 994가 되고, works은 다음과 같이 됩니다.

works = [10,9000,9997,9997,9997,9997]


이제 네번째 반복입니다.

이때 tmp = 9997 - 9000 = 997 이 됩니다.

현재 i = 4이기 때문에 9번째 줄의 코드에서, tmp*i = 3988 로써 현재 n = 994 보다 크게 되므로 10~12번째줄을 수행하지 않고 13번째, else문이 실행됩니다.

그리고 14번째 줄에 의해 q = 994//4 = 248이 되고, n = 994%4 = 2 가 됩니다.

그리고 16~17번째 줄에 의해서 현재 9997의 값을 갖고있는 요소들이 q = 248 만큼 감소합니다. 즉, works가 다음과 같이 변화합니다.

works = [10,9000,9749,9749,9749,9749]


총 줄어든 값은 248 * 4 = 992 인데, 위에서 나머지 계산을 통해 n = 2가 되었습니다. 두 값을 더해서 생각해보면 이전의 n 값인 994와 같음을 알 수 있습니다.

그리고 나머지 2를 처리하기 위해 n이 0이 될때까지 works의 가장 뒤의 요소부터 1씩 감소시킵니다.


n은 i로 나눈 나머지이기 때문에 절대로 (해당 예시에서는)9749의 값을 갖는 요소를 벗어날 수 없습니다.

따라서 works는 다음과 같이 변화합니다.

works = [10,9000,9749,9749,9748,9748]


그리고 break문을 만나 반복문을 벗어나 결과를 출력하게 됩니다.



해당 알고리즘을 어떻게 간단하게 설명해야 할지 몰라서 까다로웠던 예시를 하나 들어서 설명드렸습니다.

조금이나마 간단하게 설명해본다면 처음 입력받는 works에 대해 가장 큰 값들부터 고려하여, 그 다음 큰 값과 같게하면서 줄여나가는 방법입니다. 이는 단순히 1씩 감소시키는 것이 아니라, 큰 값들의 차이만큼 감소 시키며 그 전의 큰 값들도 같이 감소시키게 됩니다. 그리고 감소시킬 값과 n을 비교해가면서 감소시킬 값이 n보다 크면 안되므로 이럴때는 n값을 지금까지 줄여나간 요소의 개수 만큼으로 나누어 그 몫을 줄여나간 요소들에 대해 동등하게 감소시키고, 나머지는 그 요소들에 대해 1씩 줄이는 방법입니다.



블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc

안녕하세요. 문범우입니다.

이번 포스팅에서는 분류나 회귀에서 사용되는 KNN(K - Nearest Neighbors) 알고리즘에 대해서 알아보도록 하겠습니다.



1. KNN(K - Nearest Neighbors)


KNN, K-최근접 이웃 알고리즘은 특정공간내에서 입력과 제일 근접한 k개의 요소를 찾아, 더 많이 일치하는 것으로 분류하는 알고리즘입니다.


말로 이해하는 것보다 아래 그림을 통해 이해하는 것이 훨씬 쉬울 것 입니다.


위의 그림과 같은 좌표공간을 예시로 확인해보겠습니다.

위에서 파란색 점으로 되어 있는 그룹을 A그룹이라고 생각하고, 주황색 점으로 되어 있는 그룹을 B라고 하겠습니다.

이때 우리는 별 모양으로 표시된 입력값이 A그룹에 속하는지, B그룹에 속하는지를 알고 싶습니다.


그리고 이럴때 사용되는 KNN 알고리즘은 다음과 같이 적용됩니다.

우선 k값을 정의해줘야 합니다. 해당 k값에 대한 설명은 조금 밑에서 드리도록 하고, 우선 k를 3이라는 값으로 정했다고 생각해보겠습니다.


그럼 우리는 입력값과 가장 근접한 k개의 요소를 찾습니다. k = 3이므로 3개의 요수를 찾아보면 다음 그림과 같습니다.


별 모양의 점을 기준으로 원을 그려서 확인함으로써, 위의 빨간색 원과 같이 파란색 점 하나와 주황색 점 두개가 선택됨을 알 수 있습니다.

그리고 이를 통해 입력값은 주황색 점의 그룹, 즉 B의 그룹과 더 일치하다는 판단을 내릴 수 있습니다.


추가적으로 생각해보면, KNN 알고리즘은 정의한 k 값에 따라서 입력값의 분류 결과가 달라질 수 있습니다.


만약 우리가 k를 3으로 두지 않고 1로 두었다면, 아래 그림과 같은 원이 그려짐으로써 입력값은 A그룹으로 분류될 수 있습니다.



 KNN 알고리즘을 사용할 때는 적절한 k값을 찾아 설정해주는 것이 중요합니다.


또한, 위와 같이 2개의 그룹에 대해 분류를 할때 만약 k값을 짝수로 한다면 어떻게 될까요?

만약 k = 4 라고 설정했다면 다음과 같은 결과가 나옵니다.


그림에서 확인할 수 있듯이, 파란색 점 2개, 주황색 점 2개가 됨으로써 입력 값이 어떠한 그룹이라는 결론을 내릴 수 없게 됩니다.


그렇다고 무조건 k를 홀수 값으로 설정해야 하는 것은 아닙니다.

위와 같이 2개의 그룹에 대한 분류를 할때는 무조건 홀수 값을 주어야 겠지만, 만약 입력값을 3개의 그룹에 대해 분류를 할때 k = 3으로 둔다면 각각의 그룹의 요소가 하나씩 포함되어 위와 같이 어떠한 그룹이라는 결론을 내지 못하는 상황이 올 수 있습니다.


즉, KNN알고리즘을 사용할때는 분류될 그룹의 종류등을 고려하여 적절한 k값을 설정해주는 것이 중요합니다.



2. 파이썬으로 구현하기


위에서 알아본 KNN 알고리즘을 파이썬으로 구현해 보았습니다.

* 코드는 jupyter notebook에서 실행하였습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib nbagg
 
A_x_list = [0,2,4,1,1,4]
A_y_list = [4,1,5,5,2,6]
A_x = np.array(A_x_list)
A_y = np.array(A_y_list)
 
B_x_list = [7,7,5,7,10,9]
B_y_list = [4,0,2,2,3,3]
B_x = np.array(B_x_list)
B_y = np.array(B_y_list)
 
finding_point = [5,4]
 
plt.figure()
plt.scatter(A_x,A_y)
plt.scatter(B_x,B_y)
plt.scatter(finding_point[0],finding_point[1], marker='*')
 
plt.show()
cs


먼저 입력값과 기존에 존재하는 A그룹, B그룹을 좌표상으로 나타내보았습니다.

위의 코드를 통해 나오는 그래프는 아래와 같이, 우리가 위에서 살펴봤던 예제 그림과 같습니다.



그리고 실제로 입력값을 KNN 알고리즘에 적용시켜 분류하는 코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# L리스트에서 c번째 작은 값 찾는 함수
def count_min_value(L,c):
    temp = L.copy()
    temp.sort()
    item = temp[c-1]
    return L.index(item),item
 
# 입력값이 A그룹인지 B그룹인지 찾는 함수::KNN Algorithm 적용
def finding_AorB(k,x,y):
    numA = 0
    numB = 0
    A_xy = []
    B_xy = []
    # x,y 좌표가 따로 있는 것을 하나의 리스트로 통합
    for i in range(len(A_x_list)):
        A_xy.append([A_x_list[i],A_y_list[i]])
    for i in range(len(B_x_list)):
        B_xy.append([B_x_list[i],B_y_list[i]])
 
    A_distance = []
    B_distance = []
    # x,y 좌표에 대해 입력값과의 거리 산출
    for each in A_xy:
        dis = ((each[0- x)**2 + (each[1- y)**2)**(1/2)
        A_distance.append(dis)
    for each in B_xy:
        dis = ((each[0- x)**2 + (each[1- y)**2)**(1/2)
        B_distance.append(dis)
    A_result = []
    B_result = []
    
    A_min_count = 1
    B_min_count = 1
    while(numA + numB < k):
        min_A = 99999
        min_B = 99999
 
        _, min_A = count_min_value(A_distance,A_min_count)
        _, min_B = count_min_value(B_distance,B_min_count)
 
        if min_A < min_B:
            numA += 1
            A_min_count += 1
            A_result.append(A_xy[A_distance.index(min_A)])
            A_distance[A_distance.index(min_A)] = -1
        elif min_A > min_B:
            numB += 1
            B_min_count += 1
            B_result.append(B_xy[B_distance.index(min_B)])
            B_distance[B_distance.index(min_B)] = -1
        elif min_A == min_B:
            numA += 1
            numB += 1
            A_min_count += 1
            B_min_count += 1
            A_result.append(A_xy[A_distance.index(min_A)])
            A_distance[A_distance.index(min_A)] = -1
            B_result.append(B_xy[B_distance.index(min_B)])
            B_distance[B_distance.index(min_B)] = -1
    if numA > numB:
        print("RESULT: The point is A")
    elif numA < numB:
        print("RESULT, The point is B")
    elif numA == numB:
        print("I DON'T KNOW")
    print("A point is",A_result,"\nB point is",B_result,"\n")
            
cs


위의 코드를 통해 k = 1, 2, 3, 4 로 실행 시켜보면 아래와 같은 결과가 나옵니다.




이렇게 하여 KNN 최근접 이웃 알고리즘에 대해서 알아보았습니다.

위에서 구현된 파이썬 코드는 아래 github주소에 있습니다 :)


https://github.com/doorBW/KNN-Algorithm-by-python


오류가 발생하거나 잘못된 점이 있다면 언제든지 피드백 해주시길 바랍니다.

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요. 문범우입니다.

이번 포스팅에서는 파이썬에서 사용되는 컴프리헨션(Comprehension)이라는 개념에 대해서 알아보도록 하겠습니다.



1. 컴프리헨션(Comprehension)이란?


일단, 파이썬에서 사용되는 Comprehension이 무엇인지 알아보기 전에, 어떤 의미를 가지고 있는 단어인지 살펴보았습니다.

사전적으로는 이해, 이해력, 포용, 포용력, 포함, 압축 등의 뜻을 가지고 있습니다.

단순히 이런 의미로는 대체 어떻게 파이썬에서 사용되는지 감이 쉽게 안오실텐데, 하나씩 천천히 살펴보시면 충분히 이해하실 수 있을 것입니다.


앞으로 알아보는 Comprehension을 보다 제대로 이해하기 위해서는 기본적으로 파이썬의 조건문, 반복문 등의 개념을 알고 있으셔야 하며 해당 개념은 리스트, 집합(set), 딕셔너리(dictionary) 자료형에 대해 사용될 수 있기 때문에, 해당 자료형에 대해서도 알아야 제대로 이해할 수 있습니다.


2. List, Set, Dict Comprehension


먼저 알아볼 내용은 List, Set, Dict 자료형으로 사용되는 Comprehension입니다.

사용되는 방법이나, 문법등은 동일하나 어떤 자료형에 적용시키는지에 따라 다르기 때문에, 대표적으로는 리스트자료형으로 Comprehension을 알아보도록 하겠습니다.


List Comprehension은 쉽게 생각하면, 반복되거나 특정 조건을 만족하는 리스트를 보다 쉽게 만들어 내기 위한 방법입니다.



2-1. 반복문을 사용한 Comprehension


실제로 어떻게 사용되는지 먼저 간단한 예제를 통해 확인해보겠습니다.



위의 코드를 보시면 총 3번의 Comprehension이 사용되었습니다.

그 동안 우리는 특정 리스트를 생성하기 위해서 직접 값을 넣어주거나, 기타 다른 함수들을 이용했지만 Comprehension이라는 개념을 사용하면 위와 같이 쉽게 리스트를 생성할 수 있습니다.


첫번째 코드, [i for i in range(10)] 은 사실상 단순히 range()를 이용해도 되지만, Comprehension이 어떻게 사용되는지를 보여드리기 위해 보여드렸습니다.



2-2. 조건문을 사용한 Comprehension


위에서 살펴본 것은 반복문을 사용한 Comprehension 이며 반복문 이외에도 조건문을 추가로 이용하여 리스트를 생성할 수 있습니다.



위와 같이, 반복문과 조건문을 함께 사용하여 리스트를 생성한 모습입니다.


먼저 반복문을 적어주고, 반복문의 인자가 뒤에 적어준 조건문에 해당하는지를 확인하여 그 인자를 리스트의 요소로 가지게 되는 것입니다.


또한, 위에서 사용한 반복문이나 조건문을 여러개 이용하는 것도 가능합니다.

이때, 여러번 사용되는 반복문은 각각이 개별적으로 돌아가는 것이 아니고 조건문은 서로 and연산으로 묶인다고 생각하시면 됩니다.

아래 예시들을 통해 확인해보도록 하겠습니다.



2-3. 두 개의 반복문 사용하기


먼저, 두개의 반복문이 사용된 예제입니다.



먼저 반복문이 어떻게 돌아가는지 보다 잘 확인하기 위해 a,b,c,d,e 를 가진 리스트 a와 1,2,3,4,5를 가진 리스트 b를 만들어 주었습니다.

그리고 [i+j for i in a for j in b] 라는 Comprehension 구문으로 새로운 리스트를 생성해보았습니다.

해당 결과를 보면 먼저 앞의 for문에서 하나의 요소에 대해 뒤의 for문을 적용하는 방식임을 확인할 수 있습니다.

보다 쉽게 보자면, 아래와 같은 것임을 알 수 있습니다.



위의 코드를 보시면, 우리가 전에 알던 for문의 문법을 이용하여 a, b 리스트에 있는 요소를 하나씩 꺼내어 새로운 리스트에 삽입하는 방식으로 결과를 확인해보았습니다.



2-4. 두 개의 조건문 사용하기


이번에는 두개의 조건문이 사용된 예제를 확인해보도록 하겠습니다.



위의 결과를 보시면 0~49의 범위에서 2로 나눴을 때 0이며, 3으로 나눴을 때 0인 요소로 새로운 리스트가 생성된 것을 확인할 수 있습니다.

즉, 우리가 적어준 두개의 조건문이 서로 and로 묶인 것을 알 수 있습니다.



2-5. 조건문에서 else 사용하기


물론 우리가 사용하는 조건문에 대해서 if 이외에 else도 함께 사용할 수 있습니다. 하지만 else if (elif)는 사용할 수 없습니다.



위의 코드의 첫번째 줄에서 볼 수 있듯이 if와 else를 함께 사용할 수 있습니다.

0~9의 숫자에 따라서 2로 나누어 떨어지면 'even'을, 그게 아니면 'odd'를 출력하게 한 구문 입니다.

하지만 그 아래줄에서 0인 숫자에 대해서는 zero를 출력하도록, elif를 사용해보았지만 오류가 발생합니다.


그렇지만, else를 사용할 수 있기때문에 elif를 직접적으로 사용할 수는 없지만, 개념적으로 elif와 동일한 구문을 사용할 수는 있습니다.



2-6. 조건문에서 elif 사용하기



위와 같이 else 뒤에서 if를 한번 더 사용함으로써, elif와 같은 기능을 갖는 구문을 만들 수 있습니다.

첫 번째 줄에서는 0이 2로 나누어떨어진다고 보기 때문에 맨 앞에 있는 if 문에 걸려 even을 출력하게 되었습니다. 이를 수정하여 두번째 코드와 같이 작성하면 우리가 기대한 값이 출력됨을 볼 수 있습니다.

물론 이러한 경우에도 else를 여러번 중복하여 사용할 수 있습니다.



위의 코드에서는 0~9의 숫자에 대해서, 1일때는 'one', 2일때는 'two', 3일때는 'three', 4일때는 'four'를 출력하도록 여러개의 else와 if를 사용하였습니다. 그리고 그외에는 'hum'을 출력하도록 설정하였습니다.



3. Generator Expression


이번에는 Generator Expression에 대해서 알아봅니다.

지금까지 Comprehension에 대해서 알아보다가 갑자기 다른 주제라서 당황하셨나요?

하지만 Generator Expression도 Comprehension를 사용한 기능 중에 하나입니다.


처음에, Comprehension도 리스트말고도 {}를 사용하는 집합 자료형과 키와 값을 이용한 딕셔너리 자료형에서도 이용할 수 있다고 말씀드렸습니다.

그리고 이와 비슷하게, ()를 사용하면 Generator Expression이 되는 것 입니다.

표현하는 방법 자체는 위에서 알아본 Comprehension과 동일하니 어떻게 사용되는 것인지만 예제를 통해서 알아보도록 하겠습니다.



먼저 a와 b에 우리가 위에서 알아본 Comprehension 의 구문과 소괄호, ()를 이용하여 정의하였습니다. 그리고 각각을 print해보니 generator 객체가 나왔습니다.

이처럼 Comprehension 구문을 사용하여 소괄호로 묶어주면 자동적으로 파이썬에서 generator expression으로 인식하여 generator 객체를 생성하게 됩니다.

이를 사용하기 위해서는 해당 generator객체를 next로 감싸서 출력해주면 됩니다.


우리가 Comprehension를 통해 기대하는 값들이 순서대로 하나씩 출력되는 것을 볼 수 있습니다. 그리고, 모든 값에 대해 한바퀴를 돌게 되면 아래와 같이 Stop Iteration 이라는 오류를 출력하게 됩니다.




4. Comprehension 정리


우리는 위에서 Comprehension에 대해서 알아보았습니다.

그 구문과 사용법을 익히면, list, set, dict과 같은 자료형에서 다양하게 사용될 수 있으며, 소괄호를 이용하여 generator 객체를 만들어 이용할 수도 있습니다.

이러한 구문이 나왔을 때 당황하지 않고 리딩할 수 있어야 하며, 필요할때는 적극적으로 이용할 수 있어야 하기 때문에 꼭 한번씩 인터프리터등을 이용해 연습해보시기를 추천드립니다.

마지막으로 Comprehension 구문에 대해서 총정리를 해보면, 아래와 같습니다.




블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc

Pandas_clear


안녕하세요. 문범우입니다.

이번 포스팅에서는 파이썬 라이브러리인 pandas(판다스)에 대해서 알아보도록 하겠습니다.


해당 내용은 flearning의 김길호님의 강의를 바탕으로 작성되었습니다.

https://www.flearning.net/courses/6



1. Pandas 란?


Pandas는 파이썬에서 사용하는 데이터분석 라이브러리로, 행과 열로 이루어진 데이터 객체를 만들어 다룰 수 있게 되며 보다 안정적으로 대용량의 데이터들을 처리하는데 매우 편리한 도구 입니다.


먼저 pandas를 사용하기 위해서는 pandas를 설치한 이후에 아래와 같이 import를 해야 합니다.


import pandas as pd


아래 실습에서는 jupyter notebook을 사용하였습니다.



Pandas 기초

1. Pandas 란?

In [2]:
# pandas 사용하기
import numpy as np # numpy 도 함께 import
import pandas as pd

2. Pandas 자료구조

Pandas에서는 기본적으로 정의되는 자료구조인 Series와 Data Frame을 사용합니다.

이 자료구조들은 빅 데이터 분석에 있어서 높은 수준의 성능을 보여줍니다.

2-1. Series

먼저 Series에 대해서 알아보도록 하겠습니다.

In [3]:
# Series 정의하기
obj = pd.Series([4, 7, -5, 3])
obj
Out[3]:
0    4
1    7
2   -5
3    3
dtype: int64
In [4]:
# Series의 값만 확인하기
obj.values
Out[4]:
array([ 4,  7, -5,  3])
In [5]:
# Series의 인덱스만 확인하기
obj.index
Out[5]:
RangeIndex(start=0, stop=4, step=1)
In [7]:
# Series의 자료형 확인하기
obj.dtypes
Out[7]:
dtype('int64')
In [8]:
# 인덱스를 바꿀 수 있다.
obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
obj2
Out[8]:
d    4
b    7
a   -5
c    3
dtype: int64
In [10]:
# python의 dictionary 자료형을 Series data로 만들 수 있다.
# dictionary의 key가 Series의 index가 된다
sdata = {'Kim': 35000, 'Beomwoo': 67000, 'Joan': 12000, 'Choi': 4000}
obj3 = pd.Series(sdata)
obj3
Out[10]:
Kim        35000
Beomwoo    67000
Joan       12000
Choi        4000
dtype: int64
In [12]:
obj3.name = 'Salary'
obj3.index.name = "Names"
obj3
Out[12]:
Names
Kim        35000
Beomwoo    67000
Joan       12000
Choi        4000
Name: Salary, dtype: int64
In [13]:
# index 변경
obj3.index = ['A', 'B', 'C', 'D']
obj3
Out[13]:
A    35000
B    67000
C    12000
D     4000
Name: Salary, dtype: int64

2-2. Data Frame

이번에는 Data Frame에 대해서 알아보도록 하겠습니다.

In [23]:
# Data Frame 정의하기
# 이전에 DataFrame에 들어갈 데이터를 정의해주어야 하는데,
# 이는 python의 dictionary 또는 numpy의 array로 정의할 수 있다.
data = {'name': ['Beomwoo', 'Beomwoo', 'Beomwoo', 'Kim', 'Park'],
        'year': [2013, 2014, 2015, 2016, 2015],
        'points': [1.5, 1.7, 3.6, 2.4, 2.9]}
df = pd.DataFrame(data)
df
Out[23]:
name year points
0 Beomwoo 2013 1.5
1 Beomwoo 2014 1.7
2 Beomwoo 2015 3.6
3 Kim 2016 2.4
4 Park 2015 2.9
In [24]:
# 행과 열의 구조를 가진 데이터가 생긴다.
In [25]:
# 행 방향의 index
df.index
Out[25]:
RangeIndex(start=0, stop=5, step=1)
In [26]:
# 열 방향의 index
df.columns
Out[26]:
Index(['name', 'year', 'points'], dtype='object')
In [27]:
# 값 얻기
df.values
Out[27]:
array([['Beomwoo', 2013, 1.5],
       ['Beomwoo', 2014, 1.7],
       ['Beomwoo', 2015, 3.6],
       ['Kim', 2016, 2.4],
       ['Park', 2015, 2.9]], dtype=object)
In [28]:
# 각 인덱스에 대한 이름 설정하기
df.index.name = 'Num'
df.columns.name = 'Info'
df
Out[28]:
Info name year points
Num
0 Beomwoo 2013 1.5
1 Beomwoo 2014 1.7
2 Beomwoo 2015 3.6
3 Kim 2016 2.4
4 Park 2015 2.9
In [29]:
# DataFrame을 만들면서 columns와 index를 설정할 수 있다.
df2 = pd.DataFrame(data, columns=['year', 'name', 'points', 'penalty'],
                  index=['one', 'two', 'three', 'four', 'five'])
df2
Out[29]:
year name points penalty
one 2013 Beomwoo 1.5 NaN
two 2014 Beomwoo 1.7 NaN
three 2015 Beomwoo 3.6 NaN
four 2016 Kim 2.4 NaN
five 2015 Park 2.9 NaN

DataFrame을 정의하면서, data로 들어가는 python dictionary와 columns의 순서가 달라도 알아서 맞춰서 정의된다.

하지만 data에 포함되어 있지 않은 값은 NaN(Not a Number)으로 나타나게 되는데,

이는 null과 같은 개념이다.

NaN값은 추후에 어떠한 방법으로도 처리가 되지 않는 데이터이다.

따라서 올바른 데이터 처리를 위해 추가적으로 값을 넣어줘야 한다.

In [31]:
# describe() 함수는 DataFrame의 계산 가능한 값들에 대한 다양한 계산 값을 보여준다.
df2.describe()
Out[31]:
year points
count 5.000000 5.000000
mean 2014.600000 2.420000
std 1.140175 0.864292
min 2013.000000 1.500000
25% 2014.000000 1.700000
50% 2015.000000 2.400000
75% 2015.000000 2.900000
max 2016.000000 3.600000

3. DataFrame Indexing

In [33]:
data = {"names": ["Kilho", "Kilho", "Kilho", "Charles", "Charles"],
           "year": [2014, 2015, 2016, 2015, 2016],
           "points": [1.5, 1.7, 3.6, 2.4, 2.9]}
df = pd.DataFrame(data, columns=["year", "names", "points", "penalty"],
                          index=["one", "two", "three", "four", "five"])
df
Out[33]:
year names points penalty
one 2014 Kilho 1.5 NaN
two 2015 Kilho 1.7 NaN
three 2016 Kilho 3.6 NaN
four 2015 Charles 2.4 NaN
five 2016 Charles 2.9 NaN

3-1. DataFrame에서 열을 선택하고 조작하기

In [34]:
df['year']
Out[34]:
one      2014
two      2015
three    2016
four     2015
five     2016
Name: year, dtype: int64
In [36]:
# 동일한 의미를 갖는, 다른 방법
df.year
Out[36]:
one      2014
two      2015
three    2016
four     2015
five     2016
Name: year, dtype: int64
In [37]:
df[['year','points']]
Out[37]:
year points
one 2014 1.5
two 2015 1.7
three 2016 3.6
four 2015 2.4
five 2016 2.9
In [38]:
# 특정 열에 대해 위와 같이 선택하고, 우리가 원하는 값을 대입할 수 있다.
df['penalty'] = 0.5
In [39]:
df
Out[39]:
year names points penalty
one 2014 Kilho 1.5 0.5
two 2015 Kilho 1.7 0.5
three 2016 Kilho 3.6 0.5
four 2015 Charles 2.4 0.5
five 2016 Charles 2.9 0.5
In [42]:
# 또는
df['penalty'] = [0.1, 0.2, 0.3, 0.4, 0.5] # python의 List나 numpy의 array
In [43]:
df
Out[43]:
year names points penalty
one 2014 Kilho 1.5 0.1
two 2015 Kilho 1.7 0.2
three 2016 Kilho 3.6 0.3
four 2015 Charles 2.4 0.4
five 2016 Charles 2.9 0.5
In [44]:
# 새로운 열을 추가하기
df['zeros'] = np.arange(5)
In [45]:
df
Out[45]:
year names points penalty zeros
one 2014 Kilho 1.5 0.1 0
two 2015 Kilho 1.7 0.2 1
three 2016 Kilho 3.6 0.3 2
four 2015 Charles 2.4 0.4 3
five 2016 Charles 2.9 0.5 4
In [49]:
# Series를 추가할 수도 있다.
val = pd.Series([-1.2, -1.5, -1.7], index=['two','four','five'])
In [50]:
df['debt'] = val
In [51]:
df
Out[51]:
year names points penalty zeros debt
one 2014 Kilho 1.5 0.1 0 NaN
two 2015 Kilho 1.7 0.2 1 -1.2
three 2016 Kilho 3.6 0.3 2 NaN
four 2015 Charles 2.4 0.4 3 -1.5
five 2016 Charles 2.9 0.5 4 -1.7

하지만 Series로 넣을 때는 val와 같이 넣으려는 data의 index에 맞춰서 데이터가 들어간다.

이점이 python list나 numpy array로 데이터를 넣을때와 가장 큰 차이점이다.

In [52]:
df['net_points'] = df['points'] - df['penalty']
In [53]:
df['high_points'] = df['net_points'] > 2.0
In [54]:
df
Out[54]:
year names points penalty zeros debt net_points high_points
one 2014 Kilho 1.5 0.1 0 NaN 1.4 False
two 2015 Kilho 1.7 0.2 1 -1.2 1.5 False
three 2016 Kilho 3.6 0.3 2 NaN 3.3 True
four 2015 Charles 2.4 0.4 3 -1.5 2.0 False
five 2016 Charles 2.9 0.5 4 -1.7 2.4 True
In [55]:
# 열 삭제하기
del df['high_points']
In [56]:
del df['net_points']
del df['zeros']
In [57]:
df
Out[57]:
year names points penalty debt
one 2014 Kilho 1.5 0.1 NaN
two 2015 Kilho 1.7 0.2 -1.2
three 2016 Kilho 3.6 0.3 NaN
four 2015 Charles 2.4 0.4 -1.5
five 2016 Charles 2.9 0.5 -1.7
In [58]:
df.columns
Out[58]:
Index(['year', 'names', 'points', 'penalty', 'debt'], dtype='object')
In [59]:
df.index.name = 'Order'
df.columns.name = 'Info'
In [60]:
df
Out[60]:
Info year names points penalty debt
Order
one 2014 Kilho 1.5 0.1 NaN
two 2015 Kilho 1.7 0.2 -1.2
three 2016 Kilho 3.6 0.3 NaN
four 2015 Charles 2.4 0.4 -1.5
five 2016 Charles 2.9 0.5 -1.7

3-2. DataFrame에서 행을 선택하고 조작하기

pandas에서는 DataFrame에서 행을 인덱싱하는 방법이 무수히 많다.

물론 위에서 소개했던 열을 선택하는 방법도 수많은 방법중에 하나에 불과하다.

In [61]:
# 0번째 부터 2(3-1) 번째까지 가져온다.
# 뒤에 써준 숫자번째의 행은 뺀다.
df[0:3]
Out[61]:
Info year names points penalty debt
Order
one 2014 Kilho 1.5 0.1 NaN
two 2015 Kilho 1.7 0.2 -1.2
three 2016 Kilho 3.6 0.3 NaN
In [63]:
# tow라는 행부터 four라는 행까지 가져온다.
# 뒤에 써준 이름의 행을 빼지 않는다.
df['two':'four'] # 하지만 비추천!
Out[63]:
Info year names points penalty debt
Order
two 2015 Kilho 1.7 0.2 -1.2
three 2016 Kilho 3.6 0.3 NaN
four 2015 Charles 2.4 0.4 -1.5
In [65]:
# 아래 방법을 권장한다. 
# .loc 또는 .iloc 함수를 사용하는 방법.
df.loc['two'] # 반환 형태는 Series
Out[65]:
Info
year        2015
names      Kilho
points       1.7
penalty      0.2
debt        -1.2
Name: two, dtype: object
In [67]:
df.loc['two':'four']
Out[67]:
Info year names points penalty debt
Order
two 2015 Kilho 1.7 0.2 -1.2
three 2016 Kilho 3.6 0.3 NaN
four 2015 Charles 2.4 0.4 -1.5
In [69]:
df.loc['two':'four', 'points']
Out[69]:
Order
two      1.7
three    3.6
four     2.4
Name: points, dtype: float64
In [70]:
df.loc[:,'year'] # == df['year']
Out[70]:
Order
one      2014
two      2015
three    2016
four     2015
five     2016
Name: year, dtype: int64
In [71]:
df.loc[:,['year','names']]
Out[71]:
Info year names
Order
one 2014 Kilho
two 2015 Kilho
three 2016 Kilho
four 2015 Charles
five 2016 Charles
In [72]:
df.loc['three':'five','year':'penalty']
Out[72]:
Info year names points penalty
Order
three 2016 Kilho 3.6 0.3
four 2015 Charles 2.4 0.4
five 2016 Charles 2.9 0.5
In [73]:
# 새로운 행 삽입하기
df.loc['six',:] = [2013,'Jun',4.0,0.1,2.1]
In [74]:
df
Out[74]:
Info year names points penalty debt
Order
one 2014.0 Kilho 1.5 0.1 NaN
two 2015.0 Kilho 1.7 0.2 -1.2
three 2016.0 Kilho 3.6 0.3 NaN
four 2015.0 Charles 2.4 0.4 -1.5
five 2016.0 Charles 2.9 0.5 -1.7
six 2013.0 Jun 4.0 0.1 2.1
In [79]:
# .iloc 사용:: index 번호를 사용한다.
df.iloc[3] # 3번째 행을 가져온다.
Out[79]:
Info
year          2015
names      Charles
points         2.4
penalty        0.4
debt          -1.5
Name: four, dtype: object
In [80]:
df.iloc[3:5, 0:2]
Out[80]:
Info year names
Order
four 2015.0 Charles
five 2016.0 Charles
In [81]:
df.iloc[[0,1,3], [1,2]]
Out[81]:
Info names points
Order
one Kilho 1.5
two Kilho 1.7
four Charles 2.4
In [83]:
df.iloc[:,1:4]
Out[83]:
Info names points penalty
Order
one Kilho 1.5 0.1
two Kilho 1.7 0.2
three Kilho 3.6 0.3
four Charles 2.4 0.4
five Charles 2.9 0.5
six Jun 4.0 0.1
In [84]:
df.iloc[1,1]
Out[84]:
'Kilho'

4. DataFrame에서의 boolean Indexing

In [85]:
df
Out[85]:
Info year names points penalty debt
Order
one 2014.0 Kilho 1.5 0.1 NaN
two 2015.0 Kilho 1.7 0.2 -1.2
three 2016.0 Kilho 3.6 0.3 NaN
four 2015.0 Charles 2.4 0.4 -1.5
five 2016.0 Charles 2.9 0.5 -1.7
six 2013.0 Jun 4.0 0.1 2.1
In [87]:
# year가 2014보다 큰 boolean data
df['year'] > 2014
Out[87]:
Order
one      False
two       True
three     True
four      True
five      True
six      False
Name: year, dtype: bool
In [89]:
# year가 2014보다 큰 모든 행의 값
df.loc[df['year']>2014,:]
Out[89]:
Info year names points penalty debt
Order
two 2015.0 Kilho 1.7 0.2 -1.2
three 2016.0 Kilho 3.6 0.3 NaN
four 2015.0 Charles 2.4 0.4 -1.5
five 2016.0 Charles 2.9 0.5 -1.7
In [90]:
df.loc[df['names'] == 'Kilho',['names','points']]
Out[90]:
Info names points
Order
one Kilho 1.5
two Kilho 1.7
three Kilho 3.6
In [92]:
# numpy에서와 같이 논리연산을 응용할 수 있다.
df.loc[(df['points']>2)&(df['points']<3),:]
Out[92]:
Info year names points penalty debt
Order
four 2015.0 Charles 2.4 0.4 -1.5
five 2016.0 Charles 2.9 0.5 -1.7
In [93]:
# 새로운 값을 대입할 수도 있다.
df.loc[df['points'] > 3, 'penalty'] = 0
In [94]:
df
Out[94]:
Info year names points penalty debt
Order
one 2014.0 Kilho 1.5 0.1 NaN
two 2015.0 Kilho 1.7 0.2 -1.2
three 2016.0 Kilho 3.6 0.0 NaN
four 2015.0 Charles 2.4 0.4 -1.5
five 2016.0 Charles 2.9 0.5 -1.7
six 2013.0 Jun 4.0 0.0 2.1

5. Data

In [95]:
# DataFrame을 만들때 index, column을 설정하지 않으면 기본값으로 0부터 시작하는 정수형 숫자로 입력된다.
df = pd.DataFrame(np.random.randn(6,4))
df
Out[95]:
0 1 2 3
0 0.682000 -0.570393 -1.602829 -1.316469
1 -1.176203 0.171527 0.387018 1.027615
2 -0.263178 -0.212049 1.006856 0.096111
3 2.679378 0.347145 0.548144 0.826258
4 -0.249877 0.018721 -0.393715 0.302361
5 0.851420 -0.440360 -0.345895 1.055936
In [98]:
df.columns = ['A', 'B', 'C', 'D']
df.index = pd.date_range('20160701', periods=6)
#pandas에서 제공하는 date range함수는 datetime 자료형으로 구성된, 날짜 시각등을 알 수 있는 자료형을 만드는 함수
df.index
Out[98]:
DatetimeIndex(['2016-07-01', '2016-07-02', '2016-07-03', '2016-07-04',
               '2016-07-05', '2016-07-06'],
              dtype='datetime64[ns]', freq='D')
In [99]:
df
Out[99]:
A B C D
2016-07-01 0.682000 -0.570393 -1.602829 -1.316469
2016-07-02 -1.176203 0.171527 0.387018 1.027615
2016-07-03 -0.263178 -0.212049 1.006856 0.096111
2016-07-04 2.679378 0.347145 0.548144 0.826258
2016-07-05 -0.249877 0.018721 -0.393715 0.302361
2016-07-06 0.851420 -0.440360 -0.345895 1.055936
In [101]:
# np.nan은 NaN값을 의미한다.
df['F'] = [1.0, np.nan, 3.5, 6.1, np.nan, 7.0]
df
Out[101]:
A B C D F
2016-07-01 0.682000 -0.570393 -1.602829 -1.316469 1.0
2016-07-02 -1.176203 0.171527 0.387018 1.027615 NaN
2016-07-03 -0.263178 -0.212049 1.006856 0.096111 3.5
2016-07-04 2.679378 0.347145 0.548144 0.826258 6.1
2016-07-05 -0.249877 0.018721 -0.393715 0.302361 NaN
2016-07-06 0.851420 -0.440360 -0.345895 1.055936 7.0

NaN 없애기

In [102]:
# 행의 값중 하나라도 nan인 경우 그 행을 없앤다.
df.dropna(how='any') 
Out[102]:
A B C D F
2016-07-01 0.682000 -0.570393 -1.602829 -1.316469 1.0
2016-07-03 -0.263178 -0.212049 1.006856 0.096111 3.5
2016-07-04 2.679378 0.347145 0.548144 0.826258 6.1
2016-07-06 0.851420 -0.440360 -0.345895 1.055936 7.0
In [103]:
# 행의 값의 모든 값이 nan인 경우 그 행으 없앤다.
df.dropna(how='all')
Out[103]:
A B C D F
2016-07-01 0.682000 -0.570393 -1.602829 -1.316469 1.0
2016-07-02 -1.176203 0.171527 0.387018 1.027615 NaN
2016-07-03 -0.263178 -0.212049 1.006856 0.096111 3.5
2016-07-04 2.679378 0.347145 0.548144 0.826258 6.1
2016-07-05 -0.249877 0.018721 -0.393715 0.302361 NaN
2016-07-06 0.851420 -0.440360 -0.345895 1.055936 7.0

주의 drop함수는 특정 행 또는 열을 drop하고난 DataFrame을 반환한다.

즉, 반환을 받지 않으면 기존의 DataFrame은 그대로이다.

아니면, inplace=True라는 인자를 추가하여, 반환을 받지 않고서도

기존의 DataFrame이 변경되도록 한다.

In [104]:
# nan값에 값 넣기
df.fillna(value=0.5)
Out[104]:
A B C D F
2016-07-01 0.682000 -0.570393 -1.602829 -1.316469 1.0
2016-07-02 -1.176203 0.171527 0.387018 1.027615 0.5
2016-07-03 -0.263178 -0.212049 1.006856 0.096111 3.5
2016-07-04 2.679378 0.347145 0.548144 0.826258 6.1
2016-07-05 -0.249877 0.018721 -0.393715 0.302361 0.5
2016-07-06 0.851420 -0.440360 -0.345895 1.055936 7.0
In [107]:
# nan값인지 확인하기
df.isnull()
Out[107]:
A B C D F
2016-07-01 False False False False False
2016-07-02 False False False False True
2016-07-03 False False False False False
2016-07-04 False False False False False
2016-07-05 False False False False True
2016-07-06 False False False False False
In [109]:
# F열에서 nan값을 포함하는 행만 추출하기
df.loc[df.isnull()['F'],:]
Out[109]:
A B C D F
2016-07-02 -1.176203 0.171527 0.387018 1.027615 NaN
2016-07-05 -0.249877 0.018721 -0.393715 0.302361 NaN
In [110]:
pd.to_datetime('20160701')
Out[110]:
Timestamp('2016-07-01 00:00:00')
In [111]:
# 특정 행 drop하기
df.drop(pd.to_datetime('20160701'))
Out[111]:
A B C D F
2016-07-02 -1.176203 0.171527 0.387018 1.027615 NaN
2016-07-03 -0.263178 -0.212049 1.006856 0.096111 3.5
2016-07-04 2.679378 0.347145 0.548144 0.826258 6.1
2016-07-05 -0.249877 0.018721 -0.393715 0.302361 NaN
2016-07-06 0.851420 -0.440360 -0.345895 1.055936 7.0
In [112]:
# 2개 이상도 가능
df.drop([pd.to_datetime('20160702'),pd.to_datetime('20160704')])
Out[112]:
A B C D F
2016-07-01 0.682000 -0.570393 -1.602829 -1.316469 1.0
2016-07-03 -0.263178 -0.212049 1.006856 0.096111 3.5
2016-07-05 -0.249877 0.018721 -0.393715 0.302361 NaN
2016-07-06 0.851420 -0.440360 -0.345895 1.055936 7.0
In [113]:
# 특정 열 삭제하기
df.drop('F', axis = 1)
Out[113]:
A B C D
2016-07-01 0.682000 -0.570393 -1.602829 -1.316469
2016-07-02 -1.176203 0.171527 0.387018 1.027615
2016-07-03 -0.263178 -0.212049 1.006856 0.096111
2016-07-04 2.679378 0.347145 0.548144 0.826258
2016-07-05 -0.249877 0.018721 -0.393715 0.302361
2016-07-06 0.851420 -0.440360 -0.345895 1.055936
In [114]:
# 2개 이상의 열도 가능
df.drop(['B','D'], axis = 1)
Out[114]:
A C F
2016-07-01 0.682000 -1.602829 1.0
2016-07-02 -1.176203 0.387018 NaN
2016-07-03 -0.263178 1.006856 3.5
2016-07-04 2.679378 0.548144 6.1
2016-07-05 -0.249877 -0.393715 NaN
2016-07-06 0.851420 -0.345895 7.0

6. Data 분석용 함수들

In [115]:
data = [[1.4, np.nan],
           [7.1, -4.5],
        [np.nan, np.nan],
        [0.75, -1.3]]
df = pd.DataFrame(data, columns=["one", "two"], index=["a", "b", "c", "d"])
In [116]:
df
Out[116]:
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
In [118]:
# 행방향으로의 합(즉, 각 열의 합)
df.sum(axis=0)
Out[118]:
one    9.25
two   -5.80
dtype: float64
In [119]:
# 열방향으로의 합(즉, 각 행의 합)
df.sum(axis=1)
Out[119]:
a    1.40
b    2.60
c    0.00
d   -0.55
dtype: float64

이때, 위에서 볼 수 있듯이 NaN값은 배제하고 계산한다.

NaN 값을 배제하지 않고 계산하려면 아래와 같이 skipna에 대해 false를 지정해준다.

In [123]:
df.sum(axis=1, skipna=False)
Out[123]:
a     NaN
b    2.60
c     NaN
d   -0.55
dtype: float64
In [120]:
# 특정 행 또는 특정 열에서만 계산하기
df['one'].sum()
Out[120]:
9.25
In [121]:
df.loc['b'].sum()
Out[121]:
2.5999999999999996

pandas에서 DataFrame에 적용되는 함수들

sum() 함수 이외에도 pandas에서 DataFrame에 적용되는 함수는 다음의 것들이 있다.

count 전체 성분의 (NaN이 아닌) 값의 갯수를 계산

min, max 전체 성분의 최솟, 최댓값을 계산

argmin, argmax 전체 성분의 최솟값, 최댓값이 위치한 (정수)인덱스를 반환

idxmin, idxmax 전체 인덱스 중 최솟값, 최댓값을 반환

quantile 전체 성분의 특정 사분위수에 해당하는 값을 반환 (0~1 사이)

sum 전체 성분의 합을 계산

mean 전체 성분의 평균을 계산

median 전체 성분의 중간값을 반환

mad 전체 성분의 평균값으로부터의 절대 편차(absolute deviation)의 평균을 계산

std, var 전체 성분의 표준편차, 분산을 계산

cumsum 맨 첫 번째 성분부터 각 성분까지의 누적합을 계산 (0에서부터 계속 더해짐)

cumprod 맨 첫번째 성분부터 각 성분까지의 누적곱을 계산 (1에서부터 계속 곱해짐)

In [125]:
df2 = pd.DataFrame(np.random.randn(6, 4),
                   columns=["A", "B", "C", "D"],
                   index=pd.date_range("20160701", periods=6))
df2
Out[125]:
A B C D
2016-07-01 1.024359 0.213159 0.277114 -0.304599
2016-07-02 -1.693934 0.619885 -1.620676 0.067814
2016-07-03 -0.216422 0.659557 0.342833 -0.141637
2016-07-04 -0.873492 0.669932 -0.515944 0.280424
2016-07-05 -0.923094 -1.174652 2.678657 -0.663322
2016-07-06 -1.580576 0.539906 -1.900090 -0.428551
In [127]:
# A열과 B열의 상관계수 구하기
df2['A'].corr(df2['B'])
Out[127]:
-0.06715327766901227
In [128]:
# B열과 C열의 공분산 구하기
df2['B'].cov(df2['C'])
Out[128]:
-1.0099019967454226

정렬함수 및 기타함수

In [130]:
dates = df2.index
random_dates = np.random.permutation(dates)
df2 = df2.reindex(index=random_dates, columns=["D", "B", "C", "A"])
df2
Out[130]:
D B C A
2016-07-02 0.067814 0.619885 -1.620676 -1.693934
2016-07-06 -0.428551 0.539906 -1.900090 -1.580576
2016-07-03 -0.141637 0.659557 0.342833 -0.216422
2016-07-01 -0.304599 0.213159 0.277114 1.024359
2016-07-05 -0.663322 -1.174652 2.678657 -0.923094
2016-07-04 0.280424 0.669932 -0.515944 -0.873492
In [131]:
# index와 column의 순서가 섞여있다.
# 이때 index가 오름차순이 되도록 정렬해보자
df2.sort_index(axis=0)
Out[131]:
D B C A
2016-07-01 -0.304599 0.213159 0.277114 1.024359
2016-07-02 0.067814 0.619885 -1.620676 -1.693934
2016-07-03 -0.141637 0.659557 0.342833 -0.216422
2016-07-04 0.280424 0.669932 -0.515944 -0.873492
2016-07-05 -0.663322 -1.174652 2.678657 -0.923094
2016-07-06 -0.428551 0.539906 -1.900090 -1.580576
In [132]:
# column을 기준으로?
df2.sort_index(axis=1)
Out[132]:
A B C D
2016-07-02 -1.693934 0.619885 -1.620676 0.067814
2016-07-06 -1.580576 0.539906 -1.900090 -0.428551
2016-07-03 -0.216422 0.659557 0.342833 -0.141637
2016-07-01 1.024359 0.213159 0.277114 -0.304599
2016-07-05 -0.923094 -1.174652 2.678657 -0.663322
2016-07-04 -0.873492 0.669932 -0.515944 0.280424
In [134]:
# 내림차순으로는?
df2.sort_index(axis=1, ascending=False)
Out[134]:
D C B A
2016-07-02 0.067814 -1.620676 0.619885 -1.693934
2016-07-06 -0.428551 -1.900090 0.539906 -1.580576
2016-07-03 -0.141637 0.342833 0.659557 -0.216422
2016-07-01 -0.304599 0.277114 0.213159 1.024359
2016-07-05 -0.663322 2.678657 -1.174652 -0.923094
2016-07-04 0.280424 -0.515944 0.669932 -0.873492
In [135]:
# 값 기준 정렬하기
# D열의 값이 오름차순이 되도록 정렬하기
df2.sort_values(by='D')
Out[135]:
D B C A
2016-07-05 -0.663322 -1.174652 2.678657 -0.923094
2016-07-06 -0.428551 0.539906 -1.900090 -1.580576
2016-07-01 -0.304599 0.213159 0.277114 1.024359
2016-07-03 -0.141637 0.659557 0.342833 -0.216422
2016-07-02 0.067814 0.619885 -1.620676 -1.693934
2016-07-04 0.280424 0.669932 -0.515944 -0.873492
In [136]:
# B열의 값이 내림차순이 되도록 정렬하기
df2.sort_values(by='B', ascending=False)
Out[136]:
D B C A
2016-07-04 0.280424 0.669932 -0.515944 -0.873492
2016-07-03 -0.141637 0.659557 0.342833 -0.216422
2016-07-02 0.067814 0.619885 -1.620676 -1.693934
2016-07-06 -0.428551 0.539906 -1.900090 -1.580576
2016-07-01 -0.304599 0.213159 0.277114 1.024359
2016-07-05 -0.663322 -1.174652 2.678657 -0.923094
In [138]:
df2["E"] = np.random.randint(0, 6, size=6)
df2["F"] = ["alpha", "beta", "gamma", "gamma", "alpha", "gamma"]
df2
Out[138]:
D B C A E F
2016-07-02 0.067814 0.619885 -1.620676 -1.693934 1 alpha
2016-07-06 -0.428551 0.539906 -1.900090 -1.580576 1 beta
2016-07-03 -0.141637 0.659557 0.342833 -0.216422 3 gamma
2016-07-01 -0.304599 0.213159 0.277114 1.024359 4 gamma
2016-07-05 -0.663322 -1.174652 2.678657 -0.923094 5 alpha
2016-07-04 0.280424 0.669932 -0.515944 -0.873492 0 gamma
In [139]:
# E열과 F열을 동시에 고려하여, 오름차순으로 하려면?
df2.sort_values(by=['E','F'])
Out[139]:
D B C A E F
2016-07-04 0.280424 0.669932 -0.515944 -0.873492 0 gamma
2016-07-02 0.067814 0.619885 -1.620676 -1.693934 1 alpha
2016-07-06 -0.428551 0.539906 -1.900090 -1.580576 1 beta
2016-07-03 -0.141637 0.659557 0.342833 -0.216422 3 gamma
2016-07-01 -0.304599 0.213159 0.277114 1.024359 4 gamma
2016-07-05 -0.663322 -1.174652 2.678657 -0.923094 5 alpha
In [140]:
# 지정한 행 또는 열에서 중복값을 제외한 유니크한 값만 얻기
df2['F'].unique()
Out[140]:
array(['alpha', 'beta', 'gamma'], dtype=object)
In [142]:
# 지정한 행 또는 열에서 값에 따른 개수 얻기
df2['F'].value_counts()
Out[142]:
gamma    3
alpha    2
beta     1
Name: F, dtype: int64
In [144]:
# 지정한 행 또는 열에서 입력한 값이 있는지 확인하기
df2['F'].isin(['alpha','beta'])
# 아래와 같이 응용할 수 있다.
Out[144]:
2016-07-02     True
2016-07-06     True
2016-07-03    False
2016-07-01    False
2016-07-05     True
2016-07-04    False
Name: F, dtype: bool
In [146]:
# F열의 값이 alpha나 beta인 모든 행 구하기
df2.loc[df2['F'].isin(['alpha','beta']),:]
Out[146]:
D B C A E F
2016-07-02 0.067814 0.619885 -1.620676 -1.693934 1 alpha
2016-07-06 -0.428551 0.539906 -1.900090 -1.580576 1 beta
2016-07-05 -0.663322 -1.174652 2.678657 -0.923094 5 alpha

사용자가 직접 만든 함수를 적용하기

In [149]:
df3 = pd.DataFrame(np.random.randn(4, 3), columns=["b", "d", "e"],
                   index=["Seoul", "Incheon", "Busan", "Daegu"])
df3
Out[149]:
b d e
Seoul 0.806401 -0.101789 -0.201956
Incheon 0.666876 -2.144428 -0.013760
Busan 1.811228 0.225317 1.445495
Daegu -0.608445 0.128199 -0.325935
In [150]:
func = lambda x: x.max() - x.min()
In [151]:
df3.apply(func, axis=0)
Out[151]:
b    2.419673
d    2.369745
e    1.771431
dtype: float64
블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc



안녕하세요. 문범우입니다.

이번 포스팅을 시작으로 해서, 파이썬 기반 웹 프레임워크인 플라스크(Flask)에 대한 기초다지기 강의를 시작할 예정입니다.

해당강의는 서울시립대학교 멋쟁이사자처럼 python 스터디에서 진행되는 강의이며 블로그에 게시되는 내용들은 각 수업을 정리하여 스터디에 참석하지 못한 인원이나 복습용입니다.

(추가로 영상촬영을 생각하고 있지만 아직 실질적인 계획은 없습니다.)


해당 수업은 다음과 같은 분들을 대상으로 진행됩니다.

- HTML, CSS 에 대해서 기본적으로 알고계시는 분들(직접적으로 사용되지는 않습니다.)

- Python에 대해 관심이 있고 흥미가 있으신 분들(문법이 크게 중요시되지 않습니다. 파이썬 문법이 매우 simple하고 쉽기 때문에 중간중간 필요한 것들에 대해서는 설명드릴 예정입니다.)


해당 내용은 기본적으로 파이썬3가 설치되어 있는 기준에서 설명드리며, 맥OS를 기반으로 내용이 진행됩니다.

윈도우와 큰 차이는 없을 것으로 예상되지만 윈도우환경에서 작업하시면서 발생하시는 오류는 댓글 및 이메일을 통해 남겨주시면 빠른 시일내에 답변을 드리도록 하겠습니다.


수업에 대한 커리큘럼 및 강의자료는 아래 github 주소에 있습니다.

https://github.com/doorBW/UOS_LIKELION_python




그럼 바로 강의 내용을 진행해보도록 하겠습니다.



이번강의 내용은 위와 같은 순서로 진행됩니다.

먼저 우리가 다루고자 하는 Flask가 무엇인지 알아볼 텐데, 이에 대해 알기위해 Web Framework가 무엇인지도 매우 간단하게 알아볼 예정입니다.

이후 자신의 컴퓨터에 플라스크를 설치해보고, 기본적인 예제를 진행해보도록 하겠습니다.

이후에는 플라스크를 이용하여 두수의 곱에 대한 값을 구하는 페이지를 만들어보고, 이어서 학교의 공지사항을 크롤링하는 등 플라스크에서 외부 라이브러리를 사용해보는 연습을 해보도록 하겠습니다.


그럼, 플라스크는 과연 무엇일까요?

슬라이드의 그림을 보면, 우리가 과학실험시간에 자주 보던 플라스크..

우리가 지금 배우고자 하는 것과는 전혀 관련이 없습니다 :) ㅎ


플라스크는 쉽게 말해서, 웹 프레임워크(Web Framework) 입니다!

파이썬 기반의 웹 프레임워크로는 플라스크 이외에도 유명한 장고(Django)가 있습니다.

이 두가지에 대한 차이점은 우리가 웹 프레임워크가 무엇인지 먼저 알아보고 확인해볼게요!


웹 프레임워크...가 무엇일까요?

뭔가 자주 들어본 것 같긴한데!

멋쟁이 사자처럼을 하신 분들은 Ruby on Rails를 해보셨을텐데, 이것도 루비언어 기반의 웹 프레임워크입니다.


웹 프레임워크를 너무 어렵게 보지 말고 조금만 더 쉽게 생각해볼게요!


루비온레일즈를 다뤄보신분들은 조금 더 이해가 쉽겠지만, 우리가 웹 어플리케이션, 즉 웹 사이트를 만들때 해야될 것들이 뭐가 있을까요?

보통 처음에 ' http:// main. ~ . com' 이라는 주소로 들어오면 메인 화면을 띄어줄거에요! 그리고 거기서 음.. '게시판' 메뉴를 누르면, 'http:// main. ~ .com/post '이런 정도의 url로 넘어가겠죠!

근데 이런 설정은 누가하죠?

우리가 html에서 a태그를 이용해서 어디로 가는지 알려주죠, 근데 좀더 자세히 들어가서, 게시판에서는 우리가 단지 html로 작성된 내용만 보는게 아니잖아요?

사용자들은 자신이 게시글을 올릴 수도 있어야 하고, 게시글을 확인도 할 수 있어야하고~ 댓글도 달고, 좋아요도 누를수 있어야하죠!

근데 이걸 단순히 html이나 css를 통해서 구현이 가능할까요?


이렇게 우리가 어떤 화면으로 넘어가는지 경로를 설정해주고, 그 경로로 가서 단순히 정보를 보여주는 것 뿐 아니라, 내부적으로 어떤 처리를 할지, 어떤 함수를 실행할지 설정하고, 우리의 데이터베이스에 있는 내용을 어떻게 가져와서 어떻게 보여주게 할 것인지, 우리가 처음부터 하나씩 다~ 구현하려면.. 어떻게 하죠..?


그런데 이를 보다 쉽게 다룰 수 있도록 해주는 것이 바로 Flask이고 웹 프레임워크입니다!


적절한 비유가 될지는 모르겠지만, 제가 청량리에서 신촌으로 무언가 물건을 보내려할때 우리는 '퀵 서비스'를 이용할 수 있어요!

이런 서비스가 없다면, 저는 옷을 차려입고 나가서 집앞 버스정류장에 가서 272, 271 등의 버스를 타고 신촌으로 열심히 가서 알맞은 위치에 물건을 전해줘야 하죠. 하지만 '퀵 서비스'가 있기 때문에, '퀵 서비스'의 이용법만 안다면 아주 간편하게 물건을 전송할 수 있죠! (물론 요금을 지불해야하지만, 웹 프레임워크는 무료랍니다!)


이렇게, 우리가 웹 어플리케이션을 만들고자 하는데 필요한 다양한 설정이나 기능들을 보다 쉽고 간편하게 다룰 수 있도록 해주는 것이 웹 프레임워크입니다.

그리고 세계에 존재하는 다양한 언어별로 사용되는 웹 프레임워크가 여러 종류인 것이죠.

처음에 말씀드렸듯이, 파이썬을 기반으로 한 웹 프레임워크는 대표적으로 플라스크(Flask) 와 장고(Django)가 있습니다.


이 두개의 차이점에 대해서는 쉽게 생각해서,

플라스크는 덜 도와주지만 그만큼 자유도가 높고,

장고는 더 도와주지만 상대적으로 제한된 점이 있어요.


물론 우리와 같이 처음 시작해보는 사람들에게는 두가지 모두 큰차이는 없습니다.

저는 오히려 처음 할때는 깔끔한 플라스크가 조금 더 쉽게 느껴지지 않을까 라는 생각에 플라스크로 수업을 진행하게 되었습니다.



플라스크는 설치도 너무나 간편합니다.

파이썬을 설치하신 분들께서는 pip라는 파이썬 패키지툴이 자동으로 설치되어 있습니다.

이를 통해서 플라스크를 설치하시면 되는데, 위의 슬라이드와 같이


pip install flask


명령어를 맥은 터미널에, 윈도우는 cmd창에 입력시켜주세요!


* 파이썬을 이용하시는 많은 분들께서는 virtualenv 라는 가상환경을 통해 설치하시는 분들도 많은데, 이는 버전관리나 프로젝트 관리가 용이하도록 하는 방법 중에 하나입니다. 물론 이를 익히면 더 좋겠지만, 오히려 처음에는 머리속을 더 복잡하게 할 수 있다고 생각되어 단순히 로컬(자신의 컴퓨터)에 설치하는 과정으로 진행합니다.

이에 대해서 궁금하신 분들은 댓글 및 이메일에 말씀해주시면 답변드리겠습니다. :)


플라스크가 설치가 되었나요?

그럼 자신이 사용하는 에디터를 켜서 위의 코드를 먼저 따라서 쳐볼게요!


다 치셨으면, 이제 한줄씩 무엇을 의미하는지 함께 확인해볼게요.

조금 이따 보겠지만, 위와 같이 9줄 밖에 안되는 코드인데도 불구하고, 이것은 Hello World라는 문구를 출력하는 웹페이지를 완성한 상태입니다 :)


1번줄에서는, Flask라는 패키지, 우리가 방금 설치한 그것에서 flask를 가져옵니다. 쉽게 생각해서 우리가 앞으로 flask를 쓰겠다는 이야기에요.


2번줄에서는 플라스크를 생성(실행?)하는 의미입니다. app이라는 변수에 flask 프로젝트를 초기화 시켜서 실행하겠다라는 코드입니다.


3번줄은 공백이고~ 4번줄에서는, url을 지정해주는 코드입니다.

@app.route('/'), 즉 우리가 위에서 생성한 app에 대해 route, 경로를 설정해줄건데, ('/') http:// <우리 기본 주소> /  와 같은 경로를 이야기해요!

예를 들어, @app.route('/route_test') 라고했다면?

http:// <우리 기본 주소> /route_test 와 같은 경로를 이야기한 것 이겠죠?


그럼, 이렇게 경로를 설정해서 뭐할건데?

그것이바로 다음줄에 나와있습니다.

5번줄에서 함수를 정의하는데, 이것은 바로 위에서 설정한 경로에 사용자가 요청을 보냈을때 실행되는 것 입니다.

hello_world 라는 함수를 실행할 것이고, 그 함수의 내용으로는 6번줄에 나와있습니다.

return 'Hello World!' 단순히 Hello World! 를 반환하도록 하였습니다.

즉 Hello World! 라는 문자열을 띄어주라고 한거에요.


그리고 8,9번줄은 단순히 해당 플라스크 프로젝트를 실행시키는 코드라고 이해하시면 됩니다 :)


이제 위와 같은 구조를 가지는 구구단 계산 페이지를 만들어 볼거에요.

완성된 코드는 github에 있지만.. 일단 우리는 기능적으로 구현되는 것에 초점을 둘것이기에 디자인은 전~혀 예쁘지 않습니다.. :)


(해당 내용은 코드에 따로 주석으로 설명을 달아 놓았으니 추가적인 설명은 생략합니다.)



(해당 내용은 코드에 따로 주석으로 설명을 달아 놓았으니 추가적인 설명은 생략합니다.)




다음주에는 이메일 인증을 통한 회원가입을 구현해보는 실습을 진행할거에요.

실제로 자신의 구글계정을 이용해서 이메일을 보내보도록 하고, 이후에 회원가입 직접 구현해볼거에요!


실제로 비밀번호 암호화도 시켜볼 것이고, 우리가 이메일 보내는 실습을 해냈기 때문에, 회원가입을 요청한 사람이 자신이 등록한 이메일로 전송된 메일로 인증을 성공했을 때만 정상적인 사용자로 인식되게끔 할 예정입니다.


이를 위해서는 데이터베이스를 설치해서 db에 사용자 정보를 담아야하는데, 이때문에 mysql을 연결해볼지 말지 고민중입니다... (거의 할 예정이에요..ㅎㅎㅎㅎㅎ)

하지만 우리가 db 다루는 것이 목표가 아니기 때문에, db에 대해서는 대략대략 넘어가고 우리가 구현하고자 하는 기능을 구현하는데 초점을 둘 예정입니다.



이렇게 첫번째 수업에 대한 사전정리 작업을 하였습니다.

오늘 오후 7시에 수업이 진행되는데, 이후 추가적인 내용은 해당 포스트에 추가하여 정리하겠습니다.

궁금하신 점이나 잘 해결이 안되는 점들은 언제든지 댓글 및 이메일, 카카오톡을 통해 연락해주세요 :)


블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc