반응형

알고리즘 : 어떤 문제를 풀기 위한 절차나 방법, 구체적으로 어떤 문제가 주어진 '입력' 정보를 원하는 '출력(답)' 정보로 만드는 일련의 과정을 말함.

1부터 n까지 연속한 정수의 합을 구하는 알고리즘1
1부터 n까지 숫자를 차례로 더하는 방법을 사용함

def sum_n(n) :
    s = 0 # 합을 계산할 변수
    for i in range(1, n+1): # 1부터 n까지 반복(n+1은 제외)
        s = s + i
    return s
sum_n(10)

55

1부터 n까지 연속한 정수의 합을 구하는 알고리즘2
수학자 가우스의 계산 방법을 사용함

def sum_n(n):
    return n * (n+1)//2 # 슬래시 두개는 정수 나눗셈을 의미

알고리즘 1과 2를 비교했을 때, 숫자가 커지면 커질수록 알고리즘2가 더 효과적으로 동작한다는 것을 알 수 있다. 
동일한 문제를 해결하는 여러 알고리즘 중 어떤 알고리즘이 더 좋은지 판단하는 것을 '알고리즘 분석'이라고 한다.

1부터 N까지 연속한 정수의 곱을 구하는 알고리즘1

정수의 합을 구하는 알고리즘 1을 고쳐서 작성한다. 1부터 N까지의 곱은 팩토리얼(factorial)이라고 한다. 팩토리얼은 숫자 뒤에 느낌표를 붙여 표기하며 1부터 n까지 연속한 숫자를 차례로 곱한 값을 말한다. '계승'이라고도 한다.

def fact(n):
    f = 1 # 곱을 계산할 변수, 초기값은 1
    for i in range(1, n+1):
        f = f * i
    return f

1부터 N까지 연속한 정수의 곱을 구하는 알고리즘2

재귀 호출(recursion)은 어떤 함수 안에서 자기 자신을 부르는 것을 말한다. 팩토리얼을 재귀 호출로 표현하면, n! = n * (n-1)!로 표현할 수 있다.

def fact(n):
    if n <=1:
        return 1
    return n * fact(n-1)

재귀 호출에는 종료 조건이 꼭 필요하다. 종료 조건이 없으면 RecursionError 또는 Stack Olverflow 등의 에러가 발생할 수 있다.

두 자연수 a와 b의 최대공약수를 구하는 알고리즘1

최대공약수는 두 개 이상의 정수의 공통약수에서 가장 큰 값을 의미한다. 두 수의 약수 중에서, 공통된 것을 찾아, 그 값 중 최댓값을 찾는 것이다. 

def gcd(a, b):
    i = min(a, b) # 두 수 중에서 최솟값을 구하는 함수
    while True:
        if a % i == 0 b % i == 0:
            return i
        i = i -1 # i를 1만큼 감소시킨다

 

두 자연수 a와 b의 최대공약수를 구하는 알고리즘2

수학자 유클리드의 유클리드 알고리즘을 사용해서 문제를 풀 수 있다. 이때 사용하는 것이 재귀 호출이다. 

def gcd(a, b):
    if b == 0: # 종료 조건
        return a
    return gcd(b, a % b) #  좀 더 작은 값으로 자기자신을 호출

재귀 호출은 처음 사용할 때는 혼란스러울 수 있지만, 한번 익혀두면 여러가지 문제를 아주 단순하게 풀 수 있는 강력한 무기가 될 수 있다.

 

위에서 알아본 알고리즘들을 보면, 일련의 순서에 따라 함수를 작성하는 것보다 관련 알고리즘을 사용하는 것이 매우 간단하고 효율성을 높이는 것을 알 수 있다.

가장 핵심적인 알고리즘을 숙지하고 있는 것이 프로그램을 작성하는데 한단계 높은 방법을 제시한다는 것을 알고 있도록 하자.

<참고> 모두의 알고리즘 with  파이썬, 이승찬 저

반응형
반응형

Timestamp

Timestamp()는 시간의 단일 시점을 나타낸다. 파이썬 datetime.datetime 객체와 동일한 기능을 한다. date 또는 datetime  스트링과 함께 pd.Timestamp() 함수를 사용해  Timestamp 객체를 만들 수 있다.

import pandas as pd
timestamp_ex = pd.Timestamp('2023-09-10 12:00:00')

Period

Period()는 하루, 한달, 또는 일년과 같이 시간의 일정 기간을 나타낸다. date 또는 datetime  스트링과 빈도(freq)와 함께 pd.Period() 함수를 사용해  Period 객체를 만들 수 있다.

period_ex = pd.Period('2023-10', freq='M')

Timedelta

Timedelta()는 두 날짜 또는 시간들의 차이를 나타낸다. 파이썬 datetime.datetime 객체와 동일한 기능을 한다. 기간 스트링과 함께 pd.Timedelta() 함수를 사용해 Timedelta 객체를 만들 수 있다.

timedelta_ex = pd.Timedelta('1 day')

resample

resample()은 datetime index를 가지고 원하는 주기로 변환하거나 분석할 수 있다. resample()은 groupby 와 유사하게 사용할 수 있다.  데이터의 index가 datetime 같은 index가 아니라면, 이 메서드를 적용하기 전에 set_index()를 사용해, datetimeIndex를 먼저 설정해야 한다.

resample_ex = pd.resample('M').max()

shift

shift()는 데이터를 이동시킬 때 사용한다. 인수로 n을 줄 때 전체 데이터가 n행씩 뒤로 이동한다.
주식 데이터를 다룰 때 많이 사용하는데, 일간, 월간 변동률 등을 구할 때 사용한다. 다음은 일간 변동률을 구하는 것을 보여준다.

sec_dpc = (sec['Close'] / sec['Close'].shift(1) - 1) * 100 # 'Close'는 종가 필드를 나타냄

rolling

rolling()은 시리즈에서 window 크기에 해당하는 개수만큼 데이터를 추출하여 집계 함수에 해당하는 연산을 실시한다. 집계 함수로는 max(), mean(), min()을 사용할 수 있다. min_periods를 지정하면 데이터 개수가 윈도우 크기에 못미치더라도 mini_periods로 지정한 개수만 만족하면 연산을 수행한다.

kospi = pdr.get_data_yahoo('^KS11', '2023-10-04') # 주식 일별 데이터를 갖고 있다고 가정한다.

window = 252
peak = kospi['Adj Close'].rolling(window, min_periods=1).max()

 

Kaggle DataSets API

캐글에 있는 데이터셋을 가지고 연습해 보도록 한다.
캐글에서 데이터셋을 쉽게 가져오기 위해서 kaggle API를 설치하는 것이 좋다.

!pip install kaggle #kaggle 라이브러리를 설치

캐글 홈페이지에 로그인 후 본인 계정에서 API(kaggle.json) 파일을 다운로드한다.
윈도우의 경우에는 C 드라이브에서 '사용자(User)' >'컴퓨터명;>.kaggle폴더에 다운로드한 API파일을 이동시킨다.

이제 캐글 데이터셋을 주피터 노트북에서 쉽게 사용할 수 있게 되었다.
다운로드 받고자 하는 데이터셋을 캐글에서 검색한 후, API 주소를 클립보드에 복사한다(버튼을 누르도록 되어 있다).

!kaggle datasets list -s store-sales-time-series-forecasting

kaggle datasets 명령과 list, -s 옵션, 복사한 API 주소를 함께 입력하면 해당 데이터셋의 리스트를 다음과 같이 볼 수 있다.

!kaggle datasets download -w aslanahmedov/walmart-sales-forecast

분석하고 싶은 데이터셋을 선택해서 다운로드할 수 있다.
-w옵션은 현재 폴더에 저장하는 옵션이고,--unzip 옵션을 넣어 압축을 해제한 상태에서 다운로드할 수도 있다.

 

반응형
반응형

pandas는 Series, DataFrame 두개를 중심으로 데이터를 다룬다.

그 중 DataFrame에서 주로 사용하는 명령을 먼저 간단히 정리해 본 다음, loc와 iloc를 알아볼 것이다.

먼저 라이브러리를 읽어들인다.

import pandas as pd
import numpy as np

그 다음 사용할 데이터를 DataFrame 데이터 유형으로 만든다.

dates = pd.date_range('2023-09-01', periods=6)
df = pd.DataFrame(np.random.randn(6,5), index = dates, columns=['A', 'B', 'C', 'D', 'E'])
df

head() 명령을 사용하면 기본적으로 첫 5행을 보여준다. 괄호에 숫자를 기입하면 그 숫자만큼 행을 더 볼 수 있다.

df.head()

index, columns 명령을 사용하면 DataFrame의 컬럼과 인덱스를 확인할 수 있다.

df.index

df.columns

DataFrame에 값들은 values 명령을 사용해 확인하면 된다. 

df.values

info() 명령을 사용하면 DataFrame의 개요를 알 수 있다.

df.info()

describe() 명령을 사용하면 통계적 개요를 확인할 수 있다. 

df.describe()

sort_values() 명령은 by로 지정된 컬럼을 기준으로 정렬된다. ascending 옵션을 사용하면 오름차순(=True)이나 내림차순(=False)으로 정렬할 수 있다.

df.sort_values(by='C', ascending=True)

반응형

DataFrame.loc

인덱스인 dates 변수를 사용해 특정 날짜의 데이터만 보고 싶으면 df.loc 명령을 사용하면 된다. loc는 location 옵션으로 슬라이싱할 때 loc 옵션을 이용해서 위치 값을 지정할 수 있다.

df.loc[dates[0]]

컬럼을 지정한 후 모든 행을 보고 싶다면, 다음과 같이 하면 된다.

df.loc[:, ['B', 'D']]

행과 열의 범위를 모두 지정해 볼 수도 있다.

df.loc['2023-09-03':'2023-09-05', ['B', 'D']]

 

DataFrame.iloc

loc 명령과 달리 행과 열의 번호를 이용해 데이터에 바로 접근하려고 할 때는 iloc 명령을 사용한다.
iloc를 사용하면 행이나 열의 범위를 지정하면 된다. 특히 콜론(:)을 사용하면 전체 데이터를 불러온다.

아래 명령 iloc[4]는 5번째 행의 전체 컬럼 값을 불러오게 된다. 0번부터 시작하기 때문에 5번 행을 불러오게 된다.

df.iloc[4]

다음과 같이 범위를 정해서 불러올 수도 있다. 2번째 행부터 5번째 앞, 즉 2번째~4번째 행과 0번부터 2번째 열의 데이터만 가져오게 된다.

df.iloc[2:5, 0:3]

범위가 아니라 콤마(,)로 행이나 열을 지정해 데이터를 가져올 수도 있다. 행이나 열에 대해 전체를 가져오고 싶은 곳에는 그냥 콜론(:)을 사용한다.

df.iloc[1:4, :]

DataFrame에서 특정 조건을 만족하는 데이터만 가져올 수도 있다.
다음과 같이 조건을 입력해서 사용한다.

df[df.B > 0]

 

반응형
반응형

요즘 딥러닝 프로그램을 돌리는 컴퓨터가 있는 사무실에 계속 있기 어려운 환경에 있습니다. 그래서, 외부에서 컴퓨터를 온오프할 수 있도록 공유기 원격접속과 컴퓨터 WOL 기능을 켜두었습니다.

그리고 가장 많이 사용하고 있는 jupyter notebook을 원격지에서 사용할 수 있도록 세팅하였습니다.

1. 방화벽 해제 : 먼저 우분투 포트 방화벽 해제 및 공유기 포트포워드 설정을 합니다.
>> sudo ufw allow 8888(jupyter notebook의 기본포트로 원하시는 포트로 변경 가능)

2. jupyter notebook 암호 설정
로컬에서만 사용할 경우에는 보통 암호 설정 없이, 토큰을 이용해 접근했었습니다. 그렇지만 원격 접속은 보안을 위해 암호를 설정하는 것이 더 편하고 좋습니다. 
jupyter 서버 기본 설정을 위한 jupyter_notebook_config.py을 만들고, 패스워드를 설정합니다.
>> jupyter notebook --generate-config
Writing default config to: /{MyPath}/.jupyter/jupyter_notebook_config.py

>> jupyter notebook password
Enter password : ****
Verify password : ****
[NotebookPasswordApp] Wrote hashed password to /{Mypath}/.jupyter/jupyter_notebook_config.json

반응형

3. jupyter notebook 설정 파일 수정
nano ~/.jupyter/jupyter_notebook_config.py
수정할 코드를 ctrl+w(검색)를 사용해 찾은 후, 수정합니다.

c.NotebookApp.allow_origin = '*'  # 외부 접속을 허용합니다.
c.NotebookApp.notebook_dir = '작업 절대경로' # 작업경로를 설정합니다.
c.NotebookApp.ip = '*' # 외부 접속 포트 전체 오픈. 보안을 위해 특정 IP만 열어두셔도 됩니다.
c.NotebookApp.port = '8888' # 외부접속 사용 포트를 설정합니다.
c.NotebookApp.password = '<해시화된 패스워드 입력>' # 암호설정시 만들어진 jupyter_notebook_config.json을 nano등으로 열면 해시화된 패스워드를 확인할 수 있습니다.
c.NotebookApp.password_required = True # 비밀번호를 요구하도록 합니다.
c.NotebookApp.open_browser = False # 서버 pc에서 자동으로 브라우저가 열리지 않도록 합니다.

4. 서버 실행
>> jupyter notebook
이제 외부에서 접속하시면 됩니다. 공유기에서 ddns를 설정한 경우, ddns:포트번호(여기선 8888)을 주소창에 입력하면 접속할 수 있습니다. 
그 다음 설정한 비밀번호를 입력하면 로그인이 됩니다.

반응형
반응형

git 기본 명령어(로컬)

git(깃)은 파이썬으로 작업하는 사람들이 협업하는데 있어 필수적이라고 생각한다. 명령어 사용에 익숙해질 수 있도록 기본 명령어를 정리해 봐야겠다.

- git init : 기존 디렉토리(폴더)를 git repository(저장소)로 초기화하여 만들기

- git add 파일이름 : git에 새로운 파일을 추가하여 git이 추적할 수 있게 하기

- git commit -m "<메시지>" : 변경된 파일을 저장소에 제출하기

- git status : 현재 저장소 상태를 출력하기

 

git branch(독립적인 공간을 따로 만들기)

- git branch 이름 : '이름'의 브랜치를 만들기

- git checkout 브랜치이름 : 현재 작업중인 '브랜치이름'으로 작업 공간을 변경하기

- git merge 브랜치이름 : 현재 작업중인 브랜치(보통 master)에 '브랜치이름'을 끌어와 병합하기

 

Github(원격 저장소 대표) 기본

대표적인 Git 기반의 원격 저장소로, 전세계에서 진행되는 오픈소스 프로젝트가 가장 많이 모여 있는 곳임. 

GitHub의 각 프로젝트 메인화면의 기본적인 기능은 다음과 같다.

- 포크(Fork) : 다른 사람의 저장소를 복사하기

- 풀 리퀘스트(Pull Request) : 포크한 저장소를 수정해 다시 원본 저장소에 병합해달라는 요청하는 것

- 이슈(Issues) : 저장소 안에서 사용자들 사이의 문제를 논의하는 기능

- 위키(Wiki) : 저장소와 관련된 체계적인 기록을 남기는 기능

 공개(public) 원격 저장소와 비공개(private) 원격 저장소로 구분되는데, 무료 사용자는 공개 원격 저장소를 생성해서 사용해야 한다.

 

원격 저장소와 Git 명령어

- git clone : 원격 저장소의 모든 내용을 로컬 저장소로 복사한다.

- git remote add : 로컬 저장소를 특정 원격 저장소와 연결한다.

- git push : 로컬 저장소의 내용을 보내거나 로컬 저장소의 변경 사항을 원격 저장소로 보낸다.

- git fetch : 로컬 저장소와 원격 저장소의 변경 사항이 다를 때 이를 비교 대조하고 git merge 명령어와 함께 최신 데이터를 반영하거나 충돌 문제 등을 해결한다.

- git pull : git remote 명령을 통해 서로 연결된 원격 저장소의 최신 내용을 로컬 저장소로 가져오면서 병합한다. git push와 반대 성격의 명령어이다. 

 

반응형
반응형

이번에는 pandas에서 데이터를 삭제하는 방법에 대해 알아보고자 합니다.

1. pandas, numpy 라이브러리를 불러들입니다.

>>> import pandas as pd
>>> numpy as np

2. 다음 주소에서 데이터셋을 읽어들입니다.

>>> url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data'

3. 읽어들인 데이터셋을 wine 변수에 저장합니다.

>>> wine = pd.read_csv(url)
>>> wine.head()

4. 컬럼들 중에서 첫번째, 네번째, 일곱번째, 아홉번째, 열두번째, 열세번째, 열네번째 컬럼을 삭제합니다.

>>> wine = wine.drop(wine.columns[[0, 3, 6, 8, 11, 12, 13]], axis=1) # drop 메서드를 사용해서, 컬럼을 삭제합니다. axis=1 인수로 열을 삭제한다는 것을 명시합니다.
>>> wine.head()

5. 아래와 같이 열을 지정합니다. 

1) alcohol 2) malic_acid 3) alcalinity_of_ash 4) magnesium 5) flavanoids 6) proanthocyanins 7) hue

>>> wine.columns = ['alcohol', 'malic_acid', 'alcalinity_of_ash', 'magnesium', 'flavanoids', 'proanthocyanins', 'hue']
>>> wine.head()

6. alcohol 컬럼 첫 3행의 값을 NaN으로 설정합니다.

>>> wine.iloc[0:3, 0] = np.nan # iloc 메서드로 첫번째 열(0)부터 세번째 열(2)에, 첫번째 컬럼(0)에 적용합니다. 0부터 시작입니다.

>>> wine.head()

7. magnesium 컬럼의 3, 4행 값을 NaN으로 설정합니다.

>>> wine.iloc[2:4, 3] = np.nan # iloc 메서드로 세번째 열(2)부터 네번째 열(3)에, 세번째 컬럼(3)에 적용합니다. 0부터 시작입니다. 

>>> wine.head()

8. alcohol 컬럼의 NaN 값을 10으로 채우고, magnesium 컬럼의 NaN은 100으로 채웁니다.

>>> wine.alcohol.fillna(10, inplace = True) # fillna 메서드로 NaN 값을 10으로 바꿉니다. inplace=True로 다른 객체를 만드는 게 아니라 기존 객체를 바꿉니다.
>>> wine.magnesium.fillna(100, inplace = True) 
>>> wine.head()

9. 결측 값의 숫자를 합산합니다.

>>> wine.isnull().sum() # 결측 값을 가진 데이터가 없습니다.

10. 10까지의 범위에서 랜덤으로 숫자 배열을 생성합니다.

>>> random = np.random.randint(10, size = 10)
>>> random

11. 생성한 난수를 인덱스로 사용하고 각 셀에 NaN 값을 할당합니다.

>>> wine.alcohol[random] = np.nan
>>> wine.head(10)

12. 얼마나 많은 결측 값이 있는지 확인합니다.

>>> wine.isnull().sum()

13. 결측 값을 가진 행들을 삭제합니다.

>>> wine = wine.dropna(axis=0, how = 'any') # dropna 메서드로 결측값을 찾습니다. axis=0으로 행을 선택하고, how='any'로 하나라도 결측값이 있는 경우를 포함합니다.
>>> wine.head()

14. 인덱스를 재설정합니다. 

>>> wine = wine.reset_index(drop = True)
>>> wine.head()

 

(Source : Pandas exercises 깃헙)

반응형
반응형

애플 주식 가격을 사용한 시계열 처리를 알아봅니다.

1. pandas, numpy, matplotlib 라이브러리를 불러들입니다.

>>> import pandas as pd

>>> import numpy as np

>>> import matplotlib.pyplot as plt

>>> %matplotlib inline # jupyter notebook 내에서 그래프를 그릴 수 있도록 합니다.

2. 아래 주소를 url 변수에 저장합니다.

>>> url = 'https://raw.githubusercontent.com/guipsamora/pandas_exercises/master/09_Time_Series/Apple_Stock/appl_1980_2014.csv'

3. 위 주소에서 받은 애플 주식 데이터를 apple 변수에 저장합니다.

>>> apple = pd.read_csv(url)
>>> apple.head()

4. 컬럼들의 타입을 확인합니다.

>>> apple.dtypes # dtypes 메서드를 사용해서 각 컬럼의 타입을 알 수 있습니다. 위 표에서 보면, Date 컬럼이 날짜(Date) 타입일 것 같았는데, 실제로는 object 타입인 걸 알 수 있습니다.

5. datetime 타입으로 date 컬럼을 변환합니다.

>>> apple.Date = pd.to_datetime(apple.Date) # pandas의 to_datetime 메서드로 Date 컬럼의 타입을 변경하고, Date 컬럼에 반영합니다.
>>> apple['Date'].head()

6. Date 컬럼을 인덱스로 설정합니다.

>>> apple = apple.set_index('Date') # set_index 메서드로 해당 컬럼을 인덱스로 설정합니다.
>>> apple.head()

7. 중복되는 날짜가 있는지 알아봅니다.

>>> apple.index.is_unique # 불린값으로 중복된 날짜가 없음을 알 수 있습니다.
>>> True

8. 날짜가 최신 날짜부터 나타납니다. 과거 날짜부터 나오도록 정렬합니다.

>>> apple.sort_value(ascending = True).head() # sort_value 메서드를 사용해서 정렬합니다. 

9. 매월의 마지막 영업일을 가져옵니다.

>>> apple_month = apple.resample('BM').mean()
>>> apple_month.head()

10. 첫날부터 가장 최신 일자 사이의 날짜 차이를 알아봅니다.

>>> (apple.index.max() - apple.index.min()).days

>>> 12261

11. 우리 데이타에는 얼마나 많은 달(months)이 있는지 알아봅니다.

>>> apple_months = apple.resample('BM').mean()
>>> len(apple_months.index)
>>> 404

12. '조정된 마감(Adj Close)' 값으로 그래프를 그립니다. 전체 크기는 13.5*9 인치로 정합니다.

>>> appl_open = apple['Adj Close'].plot(title = 'Apple Stock')
>>> fig = appl_open.get_figure()
>>> fig.set_size_inches(13.5, 9)

(Source : Pandas exercises 깃헙)

반응형
반응형

1. pandas, numpy, matplotlib, seaborn 등 필요한 라이브러리를 읽어들입니다. 

>>> import pandas as pd
>>> import numpy as np
>>> import matplotlib.pyplot as plt # 파이썬에서 시각화를 처리하는데 필요한 대표적인 라이브러리로 생각하면 됩니다.
>>> import seaborn as sns # matplotlib를 바탕으로 해서 시각화를 더 멋지게 만들어줍니다.
% matplotlib inline # jupyter notebook(쥬피터 노트북) 내에서 그래프를 보여주도록 해줍니다.
sns.set(style='ticks') # 더 나은 스타일로 그래프를 보여주도록 seaborn을 설정합니다.

2. 다음 주소에서 데이터셋을 읽어들입니다.

>>> path = 'https://raw.githubusercontent.com/guipsamora/pandas_exercises/master/07_Visualization/Online_Retail/Online_Retail.csv'

3. online_rt라는 변수에 데이터셋을 할당합니다.

>>> online_rt = pd.read_csv(path, encoding = 'latin1') # encoding 인수로 적합한 언어 인코딩을 적용합니다.
>>> online_rt.head()

4. 영국을 제외하고 수량(Quantity)이 가장 많은 10개 국가에 대한 히스토그램을 만듭니다.

>>> countries = online_rt.groupby('Country').sum() # groupby 메서드를 사용해 국가별 합계를 구하고 countries 변수에 저장합니다.
>>> countries = countries.sort_values(by = 'Quantity', ascending=False) # sort_values 메서드로 정렬합니다. 이때 정렬 기준은 by인수를 사용해 'Quantity'로 정하고, ascending=False를 사용해 내림차순으로 정리합니다.
>>> countries['Quantity'].plot(kind='bar') # 그래프를 만듭니다. 
>>> plt.xlabel('Countries') # x축 라벨을 정합니다.
>>> plt.ylabel('Quantity') # y축 라벨을 정합니다.
>>> plt.title('10 Countries with most orders') # 제목을 정합니다.
>>> plt.show() # 그래프를 보여줍니다.

5. 마이너스 수량은 제외하도록 처리합니다.

>>> online_rt = online_rt[online_rt.Quantity > 0] # 수량이 0보다 큰 경우에만 선택해서 online_rt 변수에 재배정합니다.
>>> online_rt.head()

6. 상위 3개 국가의 고객 ID별 단가별 수량을 사용하여 산점도를 만듭니다.

>>> customers = online_rt.groupby(['CustomerID', 'Country']).sum() # groupby 메서드를 사용해 CustomerID와 Country 컬럼에 대한 합계를 구해 customers 변수에 저장합니다.
>>> customers = customers[customers.UnitPrice > 0] # UnitPrice가 0보다 큰 데이타만 customers 변수에 재할당합니다.
>>> customers['Country'] = customers.index.get_level_values(1) # index.get_level_values() 메서드로 요청한 수준의 인덱스를 반환합니다. 레벨은 0부터 시작합니다.
>>> top_countries = ['Netherlands', 'EIRE', 'Germany']
>>> customers = customers[customers['Country'].isin(top_countries)] # top_countries에서 정한 국가들을 선택하기 위해 데이터 프레임을 필터링합니다.
>>> g = sns.FacetGrid(customers, col='Country') # FaceGrid를 만듭니다. FaceGrid가 만드는 플롯은 흔히 "격자", "격자"또는 "작은 다중 그래픽"이라고 불립니다.
>>> g.map(plt.scatter, 'Quantity', 'UnitPrice', alpha=1) # map 메서드로 각 Facet의 데이터 하위 집합에 플로팅 기능을 적용합니다.
>>> g.add_legend() # 범례를 추가합니다.

7. 앞의 결과가 왜 이렇게 보잘 것 없는지를 조사합니다.

>>> customers = online_rt.groupby(['CustomerID', 'Country']).sum()
>>> customers

7-1. 수량(Quantity)과 단가(UnitPrice)를 별도로 보는 게 의미가 있을까?

>>> display(online_rt[online_rt.CustomerID == 12347.0].sort_values(by='UnitPrice', ascending=False).head())
>>> display(online_rt[online_rt.CustomerID == 12346.0].sort_values(by='UnitPrice', ascending=False).head()) # 고객ID 12346.0번은 다른 나라와 다르게 수량이 매우 많고, 단가는 매우 낮게 나옵니다. 그 이유를 알아볼 필요가 있어서 살펴보고자 합니다. 아래 데이터에서 보듯이 고객 ID 12346.0은 단 한건의 주문으로 대량 주문한 경우가 되겠습니다. 

7-2. 6번 최초의 질문으로 돌아가 보면, '상위 3개 국가의 고객 ID별 단가별 수량을 사용하여 산점도를 만듭니다'에 대해 구체화해서 생각해 봐야 합니다. 총 판매량이냐? 아니면 총 수익으로 계산해야 할 것인가? 먼저 판매량에 따라 분석해 보도록 하겠습니다.

>>> sales_volume = online_rt.groupby('Country').Quantity.sum().sort_values(ascending=False)
>>> top3 = sales_volume.index[1:4] # 영국을 제외합니다.
>>> top3

7-3. 이제 상위 3개국을 알게 되었습니다. 이제 나머지 문제에 집중합니다. 국가 ID는 쉽습니다. groupby 메서드로 'CustomerID'컬럼을 그룹핑하면 됩니다. 'Quantity per UnitPrice'부분이 까다롭습니다. 단순히 Quantity 또는 UnitPrice로 계산하는 것은 원하는 단가별 수량을 얻기 어렵습니다. 따라서, 두개 컬럼을 곱해 수익 'Revenue'라는 컬럼을 만듭니다.

>>> online_rt['Revenue'] = online_rt.Quantity * online_rt.UnitPrice
>>> online_rt.head()

7-4. 각 국가 주문별로 평균 가격(수익/수량)을 구합니다. 

>>> grouped = online_rt[online_rt.Country.isin(top3)].groupby(['CustomerID', 'Country])
>>> plottable = grouped['Quantity', 'Revenue'].agg('sum')
>>> plottable['Country'] = plottable.index.get_level_values(1)
>>> plottable.head()

7-5. 그래프 그리기.

>>> g = sns.FacetGrid(plottable, col='Country') # FaceGrid를 만듭니다. FaceGrid가 만드는 플롯은 흔히 "격자", "격자"또는 "작은 다중 그래픽"이라고 불립니다.
>>> g.map(plt.scatter, 'Quantity', 'AvgPrice', alpha=1) # map 메서드로 각 Facet의 데이터 하위 집합에 플로팅 기능을 적용합니다.
>>> g.add_legend() # 범례를 추가합니다.

7-6. 아직 그래프의 정보가 만족스럽지 못합니다. 많은 데이타가 수량은 50000 미만, 평균 가격은 5 이하인 경우라는 것을 확인할 수 있습니다. 트렌드를 볼 수 있을 것 같은데, 이 3개국 만으로는 부족합니다.

>>> grouped = online_rt.groupby(['CustomerID'])
>>> plottable = grouped['Quantity', 'Revenue'].agg('sum')
>>> plottable['AvgPrice'] = plottable.Revenue / plottable.Quantity
>>> plt.scatter(plottable.Quantity, plottable.AvgPrice)
>>> plt.plot()

7-7. 커브를 명확하게 보기 위해 확대해 봅니다.

>>> grouped = online_rt.groupby(['CustomerID', 'Country'])
>>> plottable = grouped.agg({'Quantity' : 'sum', 'Revenue' : 'sum'})
>>> plottable['AvgPrice'] = plottable.Revenue / plottable.Quantity
>>> plt.scatter(plottable.Quantity, plottable.AvgPrice)
>>> plt.xlim(-40, 2000) # 확대하기 위해 x축을 2000까지만 표시합니다.
>>> plt.ylim(-1, 80) # y축은 80까지만 표시합니다.
>>> plt.plot() # 아래 그래프에서 우리는 인사이트를 볼 수 있습니다. 평균 가격이 높아질수록, 주문 수량은 줄어든다는 것입니다. 

8. 단가 (x) 당 수익 (y)을 보여주는 선형 차트를 그립니다.

8-1. 가격 [0~50]을 간격 1로하여 단가를 그룹화하고 수량과 수익을 그룹핑니다.

>>> price_start = 0
>>> price_end = 50
>>> price_interval = 1
>>> buckets = np.arange(price_start, price_end, price_interval)
>>> revenue_per_price = online_rt.groupby(pd.cut(online_rt.UnitPrice, buckets)).Revenue.sum()
>>> revenue_per_price.head()

8-2. 그래프를 그려봅니다.

>>> revenue_per_price.plot()
>>> plt.xlabel('Unit Price (in intervals of ' +str(price_interval_+')')
>>> plt.ylabel('Revenue')
>>> plt.show()

8-3. 좀 더 멋지게 그려보도록 합니다.

>>> revenue_per_price.plot()
>>> plt.xlabel('Unit Price (in buckets of '+str(price_interval)+')')
>>> plt.ylabel('Revenue')
>>> plt.xticks(np.arange(price_start, price_end, 3), np.arange(price_start, price_end, 3))
>>> plt.yticks(0, 500000, 1000000, 1500000, 2000000, 2500000], ['0', '$0.5M', '$1M', '$1.5M', '$2M', '$2.5M'])
>>> plt.show()

(Source : Pandas exercises 깃헙)

반응형

+ Recent posts