이번 포스팅은 쉬어가는 코너로, 

 

  - if~ elif~ else~ 조건절을 사용한 프로그래밍

  - for loop 순환문을 사용한 프로그래밍 

 

에 대한 간단한 예제 몇 가지를 소개하겠습니다. 

 

(1) 홀수, 짝수 여부 판별하기 (odd vs. even number)

(2) 소수 여부 판별하기 (prime number)

(3) 팩토리얼 계산하기 (factorial)

(4) 3&5, 3, 5 로 나누어지는 수 판별하기 (FizzBuzz)

(5) 1 ~5를 라인별로 1부터 시작해서 라인 개수 만큼 출력하기

(6) 1~15를 라인별로 라인 번호부터 시작해서 라인개수 만큼 출력하기

 

 

(1) 홀수, 짝수 여부 판별하기 (odd vs. even number)

 

## odd or even number
num = int(input("Enter a number: "))

if (num % 2) == 0:
    print("{num} is Even")
else:
    print("{num} is Odd")

# Enter a number: 6
# 6 is Even

 

 

 

(2) 소수 여부 판별하기 (prime number)

 

소수(the prime number)는 '1과 자기 자신 외의 약수를 가지지 않는 1보다 큰 자연수'를 말합니다. 

 

## Prime number or not
num = int(input("Enter a number: "))

prime_num = True
if num > 1:
    for i in range(2, num):
        if (num % i) == 0 :
            prime_num = False
            break

if prime_num == False:
    print(f"{num} is not a prime number.")
else:
    print(f"{num} is a prime number.")

# Enter a number: 47
# 47 is a prime number.

 

 

 

(3) 팩토리얼 계산하기 (factorial)

 

팩토리얼이란 어떤 양의 정수 n에 대해서, n부터 1까지의 모든 양의 정수를 곱한 값을 말합니다. 보통 "n!"로 표기하며, 팩토리얼은 수학적 계산과 조합론에서 중요한 개념입니다. 예를 들어, 5!는 5 x 4 x 3 x 2 x 1 = 120입니다. 팩토리얼은 주로 순열과 조합을 계산하거나 확률과 통계에서 사용됩니다.

 

## Factorial
num = int(input("Enter a number: "))

factorial = 1
if num < 0:
    print("Factorial does not exist for negative numbers.")
elif num == 0:
    print("The factorial of 0 is 1.")
else:
    for i in range(1, num + 1):
        factorial = factorial * i
    print(f"The factorial of {num} is {factorial}")

# Enter a number: 4
# The factorial of 4 is 24

 

 

 

(4) 3&5, 3, 5 로 나누어지는 수 판별하기 (FizzBuzz)

 

Python coding test 에 나올 법한 문제로 FizzBuzz 문제가 있습니다. 1보다 큰 양의 정수를 3과 5로 모두 나누어지면 "FizzBuzz", 3으로만 나누어지면 "Fizz", 5로만 나누어지면 "Buzz"라고 결과를 출력하는 문제입니다. 

 

# FizzBuzz Problem
# Fizz and Buzz refer to any number that is a multiple of 3 and 5
for i in range(1, 20):
    if i % 3 == 0 and i % 5 == 0:
        print("FizzBuzz")
    elif i % 3 == 0:
        print("Fizz")
    elif i % 5 == 0:
        print("Buzz")
    else:
        print(i)
        
# 1
# 2
# Fizz
# 4
# Buzz
# Fizz
# 7
# 8
# Fizz
# Buzz
# 11
# Fizz
# 13
# 14
# FizzBuzz
# 16
# 17
# Fizz
# 19

 

 

 

(5) 1 ~5를 라인별로 1부터 시작해서 라인 개수 만큼 출력하기

 

def num(n):
    for i in range(0, n):
        num = 1
        for j in range(0, i+1):
            print(num, end=" ")
            num += 1
        print("\r")
        
        
num(5)
# 1
# 1 2
# 1 2 3
# 1 2 3 4
# 1 2 3 4 5

 

 

 

(6) 1~15를 라인별로 라인 번호부터 시작해서 라인개수 만큼 출력하기

 

def num(n):
    num = 1
    for i in range(0, n):
        for j in range(0, i+1):
            print(num, end=" ")
            num += 1
        print("\r") # the first position of a line
        

num(5)
# 1
# 2 3
# 4 5 6
# 7 8 9 10
# 11 12 13 14 15

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 리스트와 사전 자료형을 이용해서 문자열과 숫자를 서로 매핑하고 변환하는 방법을 소개하겠습니다. 

(예전에 이와 비슷한 포스팅을 한적이 있는데요, 예전 거에서 한 발자국씩만 더 나가봤습니다.)

 

(1) 리스트 내 문자열을 숫자로 변환하기

(2) 리스트 내 숫자를 문자열로 변환하기

(3) 고유한 문자열에 정수를 매핑하기 

(4) 고유한 정수에 문자열을 매핑하기 

 

 

 

(1) 리스트 내 문자열 을 숫자로 변환하기

 

* 방법 1: Python의 List Comprehensionint(str) 메소드를 같이 사용하는 방법입니다. 

 

## Converting string list to integer list

str_list = ['5', '12', '8', '19', '34']

## way 1: list comprehension
int_list = [int(x) for x in str_list]

print(int_list)
# [5, 12, 8, 19, 34]

 

 

* 방법 2: map() 메소드를 사용해서 list(map(int, string_list)) 형태로 사용하는 방법입니다. 

 

## way 2: map(int, str) function
int_list2 = list(map(int, str_list))

print(int_list2)
# [5, 12, 8, 19, 34]

 

 



(2) 리스트 내 문자열을 숫자로 변환하기

 

리스트 안의 문자열을 숫자로 바꾸는 방법은 위의 (1)번에서 소개한 2가지 방법과 동일하며, int() 대신 str()을 사용해주면 됩니다. 

 

## converting integer to string

## way 1: list comprehension
[str(i) for i in int_list]
# ['5', '12', '8', '19', '34']


## way 2: map(str, int_list)
list(map(str, int_list))
# ['5', '12', '8', '19', '34']

 

 

 

(3) 고유한 문자열에 정수를 매핑하기 

 

(3-1) sorted(set(label_list)) : 먼저 문자열 리스트 에서 유일한(unique) 문자열 집합을 추출해서 정렬한 후에 

          --> {k: v+1 for v, k in enumerate(sorted(set(label_list)))} 
                : 유일한 문자열 별로 1부터 순차적으로 정수를 부여한 사전 자료형을 만듭니다 (향후 매핑 참조에 사용됨)

 

## original label list
label_list = ['cat', 'dog', 'cat', 'car', 'tree', 'tree', 'dog']

sorted(set(label_list))
# ['car', 'cat', 'dog', 'tree']


## mapping index dictionary(label key: integer value)
label_int_dict = {
    k: v+1 for v, k in enumerate(
        sorted(set(label_list))
    )
}


print(label_int_dict)
# {'car': 1, 'cat': 2, 'dog': 3, 'tree': 4}

 

 

(3-2) for x in label_list : label_list 에서 순서대로 문자열 원소값을 for loop 순환문을 통해 하나씩 가져와서 

          --> [label_int_dict[x]] : 위의 (3-1)에서 생성한 매핑 사전에서 문자열에 해당하는 정수를 매핑해주고 리스트로 만들어줍니다. 

 

## mapping label to integer index using list comprehension
mapped_int_list = [label_int_dict[x] for x in label_list]

print(mapped_int_list)
# [2, 3, 2, 1, 4, 4, 3]

 

 

 

(4) 고유한 정수에 문자열을 매핑하기 

 

(4-1) for k, v in label_int_dict.items() : 위의 (3-1)에서 생성한 문자열-정수 매핑 사전에서 Key, Value 를 불러와서 

          --> {v: k} : {Value: Key} 로 서로 키와 값의 위치를 바꾸어서 새로 사전 자료형을 만들어줍니다. 

                             (아래 (4-2)에서 매핑 사전으로 사용됨) 

 

## converting integer to label string
int_label_dict = {
    v: k for k, v in label_int_dict.items()
}

print(int_label_dict)
# {1: 'car', 2: 'cat', 3: 'dog', 4: 'tree'}

 

 

(4-2) for x in mapped_int_list : 위의 (3-2)에서 변환했던 mapped_int_list 리스트에서 정수를 하나씩 불러와서 

          --> [int_label_dict[x]] : (4-1)에서 생성한 {정수: 문자열} 매핑 사전을 이용해서 정수에 해당하는 문자열을 매핑하고 리스트로 만들어줍니다. 

 

## mapping integer to label using list comprehension
mapped_label_list = [int_label_dict[x] for x in mapped_int_list]

print(mapped_label_list)
# ['cat', 'dog', 'cat', 'car', 'tree', 'tree', 'dog']

 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python 의 Plotly 모듈을 사용해서 

 

(1) 3차원 산점도 그리기 (3D Scatter Plot using Plotly)

(2) 3차원 표면도 그리기 (3D Surface Plot using Plotly) 

 

하는 방법을 소개하겠습니다 

 

 

 

(1) 3차원 산점도 그리기 (3D Scatter Plot using Plotly)

 

3차원 산점도는 x, y, z 의 3개 축을 기준으로 3차원의 공간에 산점도를 그려서 3개 변수들 간의 관계를 분석하기 위해서 사용합니다. 마커의 크기와 색깔을 달리해서 4번째 변수의 특성을 3차원 산점도에 추가해서 그릴 수도 있습니다. 

 

Scatter3D trace 는 go.Scatter3D() 함수에 의해 반환되는 그래프 객체입니다. 3차원 산점도이기 때문에 x, y, z 의 좌표값을 반드시 넣어줘야 하며, 이들 값은 리스트(List) 또는 Array 객체를 사용합니다. 

 

아래는 싸인과 코싸인 값을 이용해서 3차원 산점도를 그려본 예입니다. 

 

import plotly.graph_objs as go
import numpy as np

z = np.linspace(0, 10, 50)
x = np.cos(z)
y = np.sin(z)

trace = go.Scatter3d(
   x = x, 
   y = y, 
   z = z,
   mode = 'markers', 
   marker = dict(
      size = 12,
      color = z, 
      colorscale = 'Bluered_r'
      )
   )

layout = go.Layout(title = '3차원 산점도 (3D Scatter plot)')

fig = go.Figure(data = [trace], layout = layout)

fig.show()

3D Scatter Plot in Python using Plotly

 

 

 

(2) 3차원 표면도 그리기 (3D Surface Plot using Plotly) 

 

3차원 표면도는 위도(x, latitude), 경도(y, longitude), 고도(z, altitude) 의 3차원 데이터를 그래프로 표현한 것입니다. 이때 3차원 데이터 값을 개별 점(indivisual points)으로 표현한 대신에 표면(surface)으로 표현하여서 3차원 데이터 간의 관계를 분석할 때 사용합니다.

 

이때 위도(x, latitude), 경도(y, longitude)는 독립변수(indepedent variable)이 되고, 고도(z, altitude)는 종속변수(dependent variable) 가 됩니다. 

 

아래 예는 Plotly의 graph_objs 메소드 중에서 go.Surface() 메소드를 사용해서 3차원 표면도를 그려면 것입니다. 

 

import numpy as np
import plotly.graph_objs as go

x = np.outer(np.linspace(-2, 2, 30), np.ones(30))
y = x.copy().T # transpose
z = np.cos(x ** 2 + y ** 2)

trace = go.Surface(x = x, y = y, z =z )
data = [trace]

layout = go.Layout(title = '3차원 표면도 (3D Surface Plot)')

fig = go.Figure(data = data, layout=layout)

fig.show()

 

 

Plotly 그래프는 interactive mode 를 지원하기 때문에 마우스 커서를 그래프 위에 가져가면 해당 좌표의 정보가 팝업으로 나타납니다. 그리고 커서를 클릭해서 위-아래-좌-우 방향으로 이동하면 3차원 표면도가 방향이 돌아가기 때문에 입체적으로 3차원 표면을 관찰할 수 있는 장점이 있습니다. 

 

 

[Reference]

* Plotly - 3D Scatter and Surface Plot
 : https://www.tutorialspoint.com/plotly/plotly_3d_scatter_and_surface_plot.htm

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다 .

행복한 데이터 과학자 되세요!  :-)

 

 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 List Comprehension 에 대해서 알아보겠습니다.

(번역하기가 애매해서 영어 원문 그대로 사용하겠습니다)

 

1. List Comprehension 이란? 

 

Python의 List Comprehension 은 기존에 존재하는 List 에서 새로운 List 를 간결하게 생성하는 방법입니다.

 

List Comprehension Syntax 는 아래와 같습니다. 

new_list = [expression for item in iterable if condition == True]

 

 

 

간단한 예를 들어서 설명해보겠습니다.  아래에 6개의 도시를 원소로 가지는 List 가 있습니다. 첫글자가 "S"로 시작하는 도시명을 원소로 가지는 새로운 List를 만든다고 했을 때, for loop 순환문과 if 조건절을 사용하는 방법이 있습니다. 

 

city_list = ["Seoul", "New York", "London", "Shanghai", "Paris", "Tokyo"]

print(city_list)
# ['Seoul', 'New York', 'London', 'Shanghai', 'Paris', 'Tokyo']


## way 1: for loop and if conditional statement
city_s_1 = []

for city in city_list:
    if "S" in city:
        city_s_1.append(city)
        
        
print(city_s_1)
# ['Seoul', 'Shanghai']

 

 

첫글자가 "S"로 시작하는 도시명을 원소로 가지는 새로운 List를 만든다고 했을 때, List Comprehension 을 이용하면 아래와 같이 아주 간결하게 코드를 짤 수 있습니다. 

 

## way 2: List Comprehension
## [expression for item in iterable if condition == True]
city_s_2 = [city for city in city_list if "S" in city]

print(city_s_2)
# ['Seoul', 'Shanghai']

 

 

 

2. 내장 range() 함수와 조건절을 사용한 List Comprehension

 

Python의 iterable 자료형으로 str, list, tuple, dictionary, set, range 등이 있는데요, 아래 예에서는 그중에서 내장 range() 함수로 0~9까지의 정수를 반복적으로 생성해서, if 조건절을 사용해 짝수로 구성된 새로운 List 를 만들어보겠습니다. 

 

## range() 함수와 List Comprehension 으로 짝수 리스트 만들기
even_list = [i for i in range(10) if i%2 == 0]

print(even_list)
# [0, 2, 4, 6, 8]

 

 

 

3. if else 조건절을 사용해서 List Comprehension 만들기

 

if else 조건절을 List Comprehension 에서 사용할 때는 if else 조건절을 앞에 써주고, for loop 순환문을 뒤에 사용해줍니다. (* 위의 2번과 순서가 뒤바뀜에 주의)

 

## 짝수는 그대로, 홀수이면 99로 치환한 리스트
## if else 조건절이 앞에 있고, for 순환문이 뒤에 있음
if_else_list = [i if i%2 == 0 else 99 for i in range(10)]

print(if_else_list)
# [0, 99, 2, 99, 4, 99, 6, 99, 8, 99]

 

 

만약 for loop 순환문을 앞에 써주고 if else 조건절을 뒤에 써서 List Comprehension 을 시도하면 SyntaxError 가 납니다. 

 

## SyntaxError: invalid syntax
[i for i in range(10) if i%2 == 0 else 99] #SyntaxError

 

 

 

4. 2D List 에 대해 중첩된 순환문(Nested for loops)을 사용해서 List Comprehension 

 

4-1.  2D List 를 1D List 로 차원 줄이기 (flattening)

 

list_2d = [[11, 12], 
           [21, 22], 
           [31, 32], 
           [41, 42]
          ]
          
print(list_2d)
# [[11, 12], [21, 22], [31, 32], [41, 42]]


## flattening
## flattening
list_1d = [i for row in list_2d for i in row]

print(list_1d)
# [11, 12, 21, 22, 31, 32, 41, 42]

 

 

4-2. 2D List 를 전치(Transpose) 하기 

 

## Transpose
list_transpose = [[row[i] for row in list_2d] for i in range(2)]

print(list_transpose)
# [[11, 21, 31, 41], [12, 22, 32, 42]]

 

 

 

5. eval() 함수에 List Comprehension 실행하기

 

Python의 eval() 함수는 동적으로 문자열 표현식을 평가하여 실행합니다. (참고: https://rfriend.tistory.com/798 )

eval() 함수에 List Comprehension 을 문자열 표현식으로 넣어서 실행할 수 있습니다. 

 

## eval() 에 list comprehension 표현식(expression)사용 가능
str_list_comprehension = "[i for i in range(10) if i%2 == 0]"

eval(str_list_comprehension)
# [0, 2, 4, 6, 8]

 

 

하지만, 바로 위의 짝수 리스트를 만드는 List Comprehension 과 동일한 과업을 for loop 순환문과 if 조건절 statement 를 문자열로 만들어서 eval() 함수에 넣어 실행하려고 하면 SyntaxError 가 발생합니다. (eval() 함수는 expression 만 평가하여 실행가능하고, statement 는 평가 불가능함)

 

## eval()에 for loop 순환문과 if 조건절 statement 사용 불가
## SyntaxError: invalid syntax
str_for_if = """
new_list = []

for i in range(10):
    if i%2 == 0:
        new_list.append(i)
"""

eval(str_for_if) # SyntaxError: invalid syntax

 

 

 

6. List Comprehension 으로 새로운 Dict 만들기

 

str 자료형은 iterable 타입으로서, 아래처럼 List Comprehension 으로 각 단위문자 별로 쪼개서 새로운 List 로 만들 수 있습니다.  

 

text = "abcde"

print([s for s in text])
# ['a', 'b', 'c', 'd', 'e']

 

 

아래의 예는 range() 함수와 text 를 iterable 하면서 zip() 으로 정수와 각 단위문자를 짝을 이루어서 for loop 순환문으로 발생시키고, 이를  {Key: Value} 로 해서 새로운 Dict 자료형을 만든 것입니다.  

 

## list comprehension을 이용해서 dictionary 만들기
text = "abcde"

{k: v for k, v in zip(range(len(text)), text)}
# {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python을 사용해서 파워포인트와 PDF 파일에서 텍스트를 추출하는 방법을 소개하겠습니다. 

 

(1) 파워포인트 파일에서 텍스트 추출하기 (Extracting text from a PowerPoint file)

(2) PDF 파일에서 텍스트 추출하기 (Extracting text from a PDF file)

 

 

예제로 사용할 파워포인트와 PDF 파일 첨부합니다. 

 

* 예제 파워포인트 파일: "서울관광명소.pptx"

서울관광명소.pptx
0.04MB

 

* 예제 PDF 파일: "서울관광명소.pdf"

서울관광명소.pdf
0.05MB

 

 

 

예제로 사용하는 "서울관광명소.pptx" 파일은 아래와 같이 텍스트로 구성되어 있습니다. 

 

파워포인트 예시

 

 

(1) 파워포인트 파일에서 텍스트 추출하기 (Extracting text from a PowerPoint file)

 

Python으로 파워포인트에 파일에서 텍스트를 추출하기 위해서 먼저 테미널에서 "python-pptx" 모듈을 설치합니다. 

 

% python -m pip install python-pptx

 

 

파워포인트 파일이 저장되어 있는 경로와 파일 이름을 설정해주고, python-pptx 모듈을 사용해서 파워포인트 파일로 부터 텍스트를 추출해보겠습니다. 

 

이때 가독성을 높이기 위해서 각 슬라이드의 제목(title)을 Key 로 하고, 각 슬라이드의 본문 내용을 Value 로 하는 사전형(Dictionary) 형태로 추출한 텍스트를 저장해보겠습니다. 

 

## setting directory and file names
base_dir = "/Users/lhongdon/Documents/" # set with yours
ppt_nm = "서울관광명소.pptx"
pdf_nm = "서울관광명소.pdf"

ppt_path = base_dir + ppt_nm
pdf_path = base_dir + pdf_nm

print(ppt_path)
print(pdf_path)
# /Users/lhongdon/Documents/서울관광명소.pptx
# /Users/lhongdon/Documents/서울관광명소.pdf


## (1) extracting text from a PowerPoint file
from pptx import Presentation

prs = Presentation(ppt_path)

# text_runs will be populated with a list of strings,
# one for each text run in presentation
text_runs = {}

for slide in prs.slides:
    text_run = []
    for shape in slide.shapes:
        if not shape.has_text_frame:
            continue
        for paragraph in shape.text_frame.paragraphs:
            for run in paragraph.runs:
                text_run.append(run.text)

    text_runs[text_run[0]] = text_run[1:]
    
    
    
 print(text_runs)

# {'서울의 관광 명소': ['서울의 랜드마크', '서울의 고궁', '서울의 미술관과 박물관'],
#  '서울의 랜드마크': ['명동성당', '익선동 한옥거리', '광화문광장 야경', '롯데월드타워', '서울광장', '청와대'],
#  '서울의 고궁': ['경복궁', '창덕궁', '창경궁', '덕수궁', '서울 한양도성', '홍인지문', '숭례문'],
#  '서울의 미술관과 박물관': ['호림박물관', '갤러리 학고재', '별마당 도서관', '전쟁기념관', '국립중앙박물관']}

 

 

 

(2) PDF 파일에서 텍스트 추출하기 (Extracting text from a PDF file)

 

Python의 PyPDF2 모듈을 이용해서 PDF 파일로 부터 텍스트를 추출하기 위해, 먼저 터미널에서 "PyPDF2" 모듈을 설치합니다.  

 

% python -m pip install PyPDF2

 

 

다음으로, PdfReader() 메소드를 사용해서 각 PDF 페이지로부터 텍스트를 추출해서 text_all 이라는 리스트에 차곡차곡 합쳐보도록 하겠습니다. 

 

from PyPDF2 import PdfReader

## initiate PdfReader
reader = PdfReader(pdf_path)

print(len(reader.pages))
# 4


## extract text from a pdf file
text_all = []

for page in reader.pages:
    text = page.extract_text()
    text_all.append(text)
    


print(text_all)
# ['서울의관광명소1.서울의랜드마크2.서울의고궁3.서울의미술관과박물관',
#  '서울의랜드마크•명동성당•익선동한옥거리•광화문광장야경•롯데월드타워•서울광장•청와대',
#  '서울의고궁•경복궁•창덕궁•창경궁•덕수궁•서울한양도성•홍인지문•숭례문',
#  '서울의미술관과박물관•호림박물관•갤러리학고재•별마당도서관•전쟁기념관•국립중앙박물관']

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 여러개의 Python 패키지, 모듈을 한꺼번에 설치하는 방법을 소개하겠습니다. 

 

(1) 설치하고자 하는 Python 패키지, 모듈 목록을 text 파일로 만들기

(2) 터미널에서 Python 패키지 목록 text 파일이 저장된 경로로 이동하기

(3) $ pip install -r requirements.txt 로 한꺼번에 Python 패키지 설치하기

(4) Python 패키지 설치 여부 확인하기

 

 

(1) 설치하고자 하는 Python 패키지, 모듈 목록을 text 파일로 만들기

 

예시로서 requirements.txt 파일에 아래의 화면 캡쳐한 것처럼 설치가 필요한 Python 패키지의 이름과 (필요 시) 버전을 적어서 정리하였습니다. 

 

Python package requirements

 

 

 

(2) 터미널에서 Python 패키지 목록 text 파일이 저장된 경로로 이동하기

 

터미널에서 shell script 를 사용해서 위의 (1)번에서 작성한 requirement.txt 파일이 저장되어 있는 파일 경로로 이동합니다. 

 

아래 예제에서는 /Users/lhongdon/Documents/my_project/requirements.txt 에 저장해두었으며,

$ cat requirements.txt 로 텍스트 파일에 정리해놓은 파이썬 모듈 리스트를 확인해볼 수 있습니다. 

 

-- current working directory
(base) lhongdon@lhongdon0MD6T ~ % pwd
/Users/lhongdon


-- display directories and files
(base) lhongdon@lhongdon0MD6T ~ % ls
??????			Hello-World		VirtualBox VMs		iCloud Drive (Archive)	postgres-data
??????.pub		Library			anaconda3		kubernetes		seaborn-data
Applications		Movies			df.csv			minikf			ssh-key-hdlee2u
Desktop			Music			examples		minikf-kubeconfig	ssh-key-hdlee2u.pub
Documents		Pictures		git-tutorial		nltk_data
Downloads		Public			github			opt
(base) lhongdon@lhongdon0MD6T ~ % 


-- move to the directory where requirements.txt file is saved
(base) lhongdon@lhongdon0MD6T ~ % cd Documents 
(base) lhongdon@lhongdon0MD6T Documents % ls
1_GPDB_DS_Training					my_project
2_KNOU								Modern-Computer-Vision-with-PyTorch-master
3_proposals							array_select
4_project							data
5_seminar							demo
7_personal							
8_Rfriend							
9_CV_PyTorch					

(base) lhongdon@lhongdon0MD6T Documents % cd my_project 
(base) lhongdon@lhongdon0MD6T my_project % 
(base) lhongdon@lhongdon0MD6T my_project % ls
requirements.txt
(base) lhongdon@lhongdon0MD6T my_project % 


-- display the contents in requirements.txt
(base) lhongdon@lhongdon0MD6T my_project % cat requirements.txt 
psycopg2
sqlalchemy==1.4.39
sql_magic
ipython-sql==0.3.9
pgspecial==1.11.5
pmdarima

(base) lhongdon@lhongdon0MD6T my_project %

 

 

 

(3) $ pip install -r requirements.txt 로 한꺼번에 Python 패키지 설치하기

 

터미널에서 requirements.txt 파일이 저장되어 있는 경로로 이동한 상태에서 

 

% pip install -r requirements.txt 

 

를 실행하면 requirements.txt 파일에 일목요연하게 정리되어있는 Python 모듈이 순차적으로 설치가 됩니다. 

 

-- installing multiple python modules
(base) lhongdon@lhongdon0MD6T my_project % pip install -r requirements.txt


Collecting psycopg2 (from -r requirements.txt (line 1))
  Downloading psycopg2-2.9.6.tar.gz (383 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 384.0/384.0 kB 667.4 kB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Requirement already satisfied: sqlalchemy==1.4.39 in /Users/lhongdon/anaconda3/lib/python3.10/site-packages (from -r requirements.txt (line 2)) (1.4.39)
Collecting sql_magic (from -r requirements.txt (line 3))
  Downloading sql_magic-0.0.4-py3-none-any.whl (10 kB)
Collecting ipython-sql==0.3.9 (from -r requirements.txt (line 4))
  Downloading ipython_sql-0.3.9-py2.py3-none-any.whl (21 kB)
Collecting pgspecial==1.11.5 (from -r requirements.txt (line 5))
  Downloading pgspecial-1.11.5.tar.gz (42 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 42.9/42.9 kB 1.3 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Collecting pmdarima (from -r requirements.txt (line 6))
  Downloading pmdarima-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl (607 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 607.3/607.3 kB 2.7 MB/s eta 0:00:00
Requirement already satisfied: greenlet!=0.4.17 in /Users/lhongdon/anaconda3/lib/python3.10/site-packages (from sqlalchemy==1.4.39->-r requirements.txt (line 2)) (2.0.1)
Collecting prettytable (from ipython-sql==0.3.9->-r requirements.txt (line 4))
  Downloading prettytable-3.8.0-py3-none-any.whl (27 kB)
Requirement already satisfied: ipython>=1.0 in /Users/lhongdon/anaconda3/lib/python3.10/site-packages (from ipython-sql==0.3.9->-r requirements.txt (line 4)) (8.10.0)
Collecting sqlparse (from ipython-sql==0.3.9->-r requirements.txt (line 4))
  Downloading sqlparse-0.4.4-py3-none-any.whl (41 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 41.2/41.2 kB 1.3 MB/s eta 0:00:00
Requirement already satisfied: six in /Users/lhongdon/anaconda3/lib/python3.10/site-packages (from ipython-sql==0.3.9->-r requirements.txt (line 4)) (1.16.0)
-- 이하 생략

 

 

 

(4) Python 패키지 설치 여부 확인하기

 

터미널에서 

% pip show [Python Module Name] 

 

을 실행하면 해당 Python 모듈의 설치 여부 및 상세 내역을 확인할 수 있습니다. 

 

-- check the details of python module installed
(base) lhongdon@lhongdon0MD6T my_project % pip show sql_magic

Name: sql-magic
Version: 0.0.4
Summary: UNKNOWN
Home-page: UNKNOWN
Author: Chris Rawles
Author-email: crawles@gmail.com
License: UNKNOWN
Location: /Users/lhongdon/anaconda3/lib/python3.10/site-packages
Requires: findspark, ipython, jupyter, pandas, sqlparse, traitlets
Required-by: 
(base) lhongdon@lhongdon0MD6T my_project % 
(base) lhongdon@lhongdon0MD6T my_project %

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이전 포스팅에서는 

 (i) 정상확률과정(stationary process)의 정의 (https://rfriend.tistory.com/691)

 (ii) 통계적 가설 검증을 통한 시계열 정상성(stationarity test) 여부 확인 (https://rfriend.tistory.com/694)

하는 방법을 소개하였습니다. 

 

ARIMA 모형과 같은 통계적 시계열 예측 모델의 경우 시계열데이터의 정상성 가정을 충족시켜야 합니다. 따라서 만약 시계열 데이터가 비정상 확률 과정 (non-stationary process) 이라면, 먼저 시계열 데이터 변환을 통해서 정상성(stationarity)을 충족시켜주어야 ARIMA 모형을 적합할 수 있습니다

 

이번 포스팅에서는 Python을 사용하여 

(1) 분산이 고정적이지 않은 경우 분산 안정화 변환 (variance stabilizing transformation, VST)

(2) 추세가 있는 경우 차분을 통한 추세 제거 (de-trend by differencing)

(3) 계절성이 있는 경우 계절 차분을 통한 계절성 제거 (de-seasonality by seaanl differencing)

하는 방법을 소개하겠습니다. 

 

 

[ 비정상확률과정을 정상확률과정으로 변환하기 (Transforming non-stationary to stationary process) ]

 

 

먼저 예제로 사용할 약 판매량 (drug sales) 시계열 데이터를 가져와서 pandas DataFrame으로 만들고, 시계열 그래프를 그려보겠습니다. 

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt 

## getting drug sales dataset 
file_path = 'https://raw.githubusercontent.com/selva86/datasets/master/a10.csv' 
df = pd.read_csv(file_path, 
                 parse_dates=['date'], 
                 index_col='date') 

df.head(12)

#               value
# date	
# 	            3.526591
# 1991-08-01	3.180891
# 1991-09-01	3.252221
# 1991-10-01	3.611003
# 1991-11-01	3.565869
# 1991-12-01	4.306371
# 1992-01-01	5.088335
# 1992-02-01	2.814520
# 1992-03-01	2.985811
# 1992-04-01	3.204780
# 1992-05-01	3.127578
# 1992-06-01	3.270523

## time series plot 
df.plot(figsize=[12, 8]) 
plt.title('Non-Stationary Process: Increasing Variance + Trend + Seasonality', 
          fontsize=16) 
plt.show()

non-stationary process

 

위의 시계열 그래프에서 볼 수 있는 것처럼, (a) 분산이 시간의 흐름에 따라 증가 하고 (분산이 고정이 아님), (b) 추세(trend)가 있으며, (c) 1년 주기의 계절성(seasonality)이 있으므로, 비정상확률과정(non-stationary process)입니다. 

 

KPSS 검정을 통해서 확인해봐도 p-value가 0.01 이므로 유의수준 5% 하에서 귀무가설 (H0: 정상 시계열이다)을 기각하고, 대립가설(H1: 정상 시계열이 아니다)을 채택합니다. 

 

## UDF for KPSS test 
from statsmodels.tsa.stattools import kpss 
import pandas as pd 

def kpss_test(timeseries): 
    print("Results of KPSS Test:") 
    kpsstest = kpss(timeseries, regression="c", nlags="auto") 
    kpss_output = pd.Series(
        kpsstest[0:3], index=["Test Statistic", "p-value", "Lags Used"] ) 
    
    for key, value in kpsstest[3].items(): 
        kpss_output["Critical Value (%s)" % key] = value 
    print(kpss_output)
    
    
## 귀무가설 (H0): 정상 시계열이다
## 대립가설 (H1): 정상 시계열이 아니다 <-- p-value 0.01

kpss_test(df)

# Results of KPSS Test:
# Test Statistic           2.013126
# p-value                  0.010000
# Lags Used                9.000000
# Critical Value (10%)     0.347000
# Critical Value (5%)      0.463000
# Critical Value (2.5%)    0.574000
# Critical Value (1%)      0.739000
# dtype: float64

 

 

 

(1) 분산이 고정적이지 않은 경우 분산 안정화 변환 (variance stabilizing transformation, VST)

 

분산이 고정적이지 않은 경우 멱 변환(Power Transformation)을 통해서 분산을 안정화(variance stabilization) 시켜줍니다. 분산이 고정적이지 않고 추세가 있는 경우 분산 안정화를 추세 제거보다 먼저 해줍니다. 왜냐하면 추세를 제거하기 위해 차분(differencing)을 해줄 때 음수(-)가 생길 수 있기 때문입니다. 

 

power transformation

 

 

원래의 시계열 데이터의 분산 형태에 따라서 적합한 멱 변환(power transformation)을 선택해서 정상확률과정으로 변환해줄 수 있습니다. 아래의 예제 시도표를 참고하세요. 

variance stabilizing transformation (power transfortion)

 

이번 포스팅에서 사용하는 예제는 시간이 흐릴수록 분산이 점점 커지는 형태를 띠고 있으므로 로그 변환(log transformation) 이나 제곱근 변환 (root transformation) 을 해주면 정상 시계열로 변환이 되겠네요. 아래 코드에서는 자연로그를 취해서 로그 변환을 해주었습니다. 

 

## Variance Stabilizing Transformation (VST) by Taking Logarithm
df_vst = np.log(df.value)

df_vst.head()

# date
# 1991-07-01    1.260332
# 1991-08-01    1.157161
# 1991-09-01    1.179338
# 1991-10-01    1.283986
# 1991-11-01    1.271408
# Name: value, dtype: float64


## plotting
df_vst.plot(figsize=(12, 8))
plt.title("Variance Stabilizing Transformation by taking Logarithm", 
          fontsize=16)
plt.show()

variance stabilizing transformation (VST)

 

 

위의 시도표를 보면 시간이 경과해도 분산이 안정화되었음을 알 수 있습니다.  KPSS 검정을 한번 더 해주면 아직 추세(trend)와 계절성(seasonality)가 남아있으므로 여전히 비정상확률과정을 따른다고 나옵니다. 

 

## 귀무가설 (H0): 정상 시계열이다  
## 대립가설 (H1): 정상 시계열이 아니다  <-- p-value 0.01 

kpss_test(df_vst)

# Results of KPSS Test:
# Test Statistic           2.118189
# p-value                  0.010000
# Lags Used                9.000000
# Critical Value (10%)     0.347000
# Critical Value (5%)      0.463000
# Critical Value (2.5%)    0.574000
# Critical Value (1%)      0.739000
# dtype: float64

 

 

 

(2) 추세가 있는 경우 차분을 통한 추세 제거 (de-trend by differencing)

 

차분(differencing)은 현재의 시계열 값에서 시차 t 만큼의 이전 값을 빼주는 것입니다. 

 

1차 차분 = Delta1_Z(t) = Z(t) - Z(t-1) 

2차 차분 = Delta2_Z(t) = Z(t) - Z(t-1) - (Z(t-1) - Z(t-2)) =  Z(t) - 2Z(t-1) + Z(t-2)

 

Python의 diff() 메소드를 사용해서 차분을 해줄 수 있습니다. 이때 차분의 차수 만큼 결측값이 생기는 데요, dropna() 메소드를 사용해서 결측값은 제거해주었습니다. 

 

## De-trend by Differencing
df_vst_diff1 = df_vst.diff(1).dropna()

df_vst_diff1.plot(figsize=(12, 8))
plt.title("De-trend by 1st order Differencing", fontsize=16)
plt.show()

de-trending by 1st order differencing

 

위의 시도표를 보면 1차 차분(1st order differencing)을 통해서 이제 추세(trend)도 제거되었음을 알 수 있습니다. 하지만 아직 계절성(seasonality)이 남아있어서 정상성 조건은 만족하지 않겠네요. 그런데 아래에 KPSS 검정을 해보니 p-value가 0.10 으로서 유의수준 5% 하에서 정상성을 만족한다고 나왔네요. ^^;

 

## 귀무가설 (H0): 정상 시계열이다  <-- p-value 0.10
## 대립가설 (H1): 정상 시계열이 아니다 

kpss_test(df_vst_diff1)

# Results of KPSS Test:
# Test Statistic            0.121364
# p-value                   0.100000
# Lags Used                37.000000
# Critical Value (10%)      0.347000
# Critical Value (5%)       0.463000
# Critical Value (2.5%)     0.574000
# Critical Value (1%)       0.739000
# dtype: float64

 

 

 

(3) 계절성이 있는 경우 계절 차분을 통한 계절성 제거 (de-seasonality by seaanl differencing)

 

아직 남아있는 계절성(seasonality)을 계절 차분(seasonal differencing)을 사용해서 제거해보겠습니다. 1년 12개월 주기의 계절성을 띠고 있으므로 diff(12) 함수로 계절 차분을 실시하고, 12개의 결측값이 생기는데요 dropna() 로 결측값은 제거해주었습니다. 

 

## Stationary Process: De-seasonality by Seasonal Differencing
df_vst_diff1_diff12 = df_vst_diff1.diff(12).dropna()

## plotting
df_vst_diff1_diff12.plot(figsize=(12, 8))
plt.title("De-seasonality by Seasonal Differencing", 
          fontsize=16)
plt.show()

de-seasonality by seasonal differencing

 

위의 시도표를 보면 이제 계절성도 제거가 되어서 정상 시계열처럼 보이네요. 아래에 KPSS 검정을 해보니 p-value 가 0.10 으로서, 유의수준 5% 하에서 귀무가설(H0: 정상 시계열이다)을 채택할 수 있겠네요.

 

## 귀무가설 (H0): 정상 시계열이다  <-- p-value 0.10
## 대립가설 (H1): 정상 시계열이 아니다 

kpss_test(df_vst_diff1_diff12)

# Results of KPSS Test:
# Test Statistic           0.08535
# p-value                  0.10000
# Lags Used                8.00000
# Critical Value (10%)     0.34700
# Critical Value (5%)      0.46300
# Critical Value (2.5%)    0.57400
# Critical Value (1%)      0.73900
# dtype: float64

 

이제 비정상 시계열(non-stationary process)이었던 원래 데이터를 (1) log transformation을 통한 분산 안정화, (2) 차분(differencing)을 통한 추세 제거, (3) 계절 차분(seasonal differencing)을 통한 계절성 제거를 모두 마쳐서 정상 시계열(stationary process) 로 변환을 마쳤으므로, ARIMA 통계 모형을 적합할 수 있게 되었습니다. 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python을 사용해서 웹사이트에서 압축파일을 다운로드해서 압축을 해제하고 데이터셋을 합치는 방법을 소개하겠습니다. 

 

세부 절차 및 이용한 Python 모듈과 메소드는 아래와 같습니다. 

 

(1) os 모듈로 다운로드한 파일을 저장할 디렉토리가 없을 경우 새로운 디렉토리 생성하기

(2) urllib.request.urlopen() 메소드로 웹사이트를 열기 

(3) tarfile.open().extractall() 메소드로 압축 파일을 열고, 모든 멤버들을 압축해제하기

(4) pandas.read_csv() 메소드로 파일을 읽어서 DataFrame으로 만들기

(5) pandas.concat() 메소드로 모든 DataFrame을 하나의 DataFrame으로 합치기

(6) pandas.to_csv() 메소드로 합쳐진 csv 파일을 내보내기

 

 

먼저, 위의 6개 절차를 download_and_merge_csv() 라는 이름의 사용자 정의함수로 정의해보겠습니다.  

 

import os
import glob
import pandas as pd
import tarfile
import urllib.request

## downloads a zipped tar file (.tar.gz) that contains several CSV files, 
## from a public website. 
def download_and_merge_csv(url: str, down_dir: str, output_csv: str):
    """
    - url: url address from which you want to download a compressed file
    - down_dir: directory to which you want to download a compressed file
    - output_csv: a file name of a exported DataFrame using pd.to_csv() method
    """
    
    # if down_dir does not exists, then create a new directory
    down_dir = 'downloaded_data'
    if os.path.isdir(down_dir):
        pass
    else:
        os.mkdir(down_dir)
        
    # Open for reading with gzip compression.
    # Extract all members from the archive to the current working directory or directory path. 
    with urllib.request.urlopen(url) as res:
        tarfile.open(fileobj=res, mode="r|gz").extractall(down_dir)
    
    # concatenate all extracted csv files
    df = pd.concat(
        [pd.read_csv(csv_file, header=None) 
         for csv_file in glob.glob(os.path.join(down_dir, '*.csv'))])
    
    # export a DataFrame to a csv file
    df.to_csv(output_csv, index=False, header=False)

 

참고로, tarfile.open(fileobj, mode="r") 에서 4개의 mode 를 지원합니다. 

tarfile(mode) 옵션
-. mode="r": 존재하는 데이터 보관소로부터 읽기 (read)
-. mode="a": 존재하는 파일에 데이터를 덧붙이기 (append)
-. mode="w": 존재하는 파일을 덮어쓰기해서 새로운 파일 만들기 (write, create a new file overwriting an existing one)
-. mode="x": 기존 파일이 존재하지 않을 경우에만 새로운 파일을 만들기 (create a new file only if it does not already exist)

* for more information on tarfile module: https://docs.python.org/3/library/tarfile.html

 

 

현재 Jupyter Notebook 커널의 디렉토리에는 아래처럼  아직 다운로드한 파일이 없습니다. 

 

jovyan@kubecon-tutorial-0:~$ pwd
/home/jovyan
jovyan@kubecon-tutorial-0:~$ 
jovyan@kubecon-tutorial-0:~$ ls
data  down_merge_csv.ipynb  kale.log  lost+found
jovyan@kubecon-tutorial-0:~$ 
jovyan@kubecon-tutorial-0:~$

 

 

 

 

이제 위에서 정의한 download_and_merge_csv() 를 사용해서 

  (a) url='https://storage.googleapis.com/ml-pipeline-playground/iris-csv-files.tar.gz' 로 웹사이트로 부터 압축파일을 열고 모든 파일들을 해제해서 

  (b) down_dir='downloaded_data' 의 디렉토리에 다운로드하고, 

  (c) output_csv='iris_merged_data.csv'  라는 이름의 csv 파일로 모든 파일을 합쳐서 내보내기

를 해보겠습니다. 

 

download_and_merge_csv(
    url='https://storage.googleapis.com/ml-pipeline-playground/iris-csv-files.tar.gz', 
    down_dir='downloaded_data', 
    output_csv='iris_merged_data.csv')

 

 

아래의 화면캡쳐처럼 'iris_merged_data.csv' 라는 이름의 csv 파일이 새로 생겼습니다. 그리고 'downloaded_data' 라는 폴더도 새로 생겼습니다. 

 

 

 

 

터미널에서 새로 생긴 'downloaded_data' 로 디렉토리를 이동한 다음에, 파일 리스트를 확인해보니 'iris-1.csv', 'iris-2.csv', 'iris-3.csv' 의 3개 파일이 들어있네요. head 로 상위의 10 개 행을 읽어보니 iris 데이터셋이군요. 

 

jovyan@kubecon-tutorial-0:~$ ls
data  downloaded_data  down_merge_csv.ipynb  iris_merged_data.csv  kale.log  lost+found
jovyan@kubecon-tutorial-0:~$ 
jovyan@kubecon-tutorial-0:~$ 
jovyan@kubecon-tutorial-0:~$ cd downloaded_data/
jovyan@kubecon-tutorial-0:~/downloaded_data$ ls
iris-1.csv  iris-2.csv  iris-3.csv
jovyan@kubecon-tutorial-0:~/downloaded_data$ 
jovyan@kubecon-tutorial-0:~/downloaded_data$ 
jovyan@kubecon-tutorial-0:~/downloaded_data$ head iris-1.csv
5.1,3.5,1.4,0.2,setosa
4.9,3.0,1.4,0.2,setosa
4.7,3.2,1.3,0.2,setosa
4.6,3.1,1.5,0.2,setosa
5.0,3.6,1.4,0.2,setosa
5.4,3.9,1.7,0.4,setosa
4.6,3.4,1.4,0.3,setosa
5.0,3.4,1.5,0.2,setosa
4.4,2.9,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
jovyan@kubecon-tutorial-0:~/downloaded_data$

 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요.  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 matplotlib 모듈을 사용해서 

 

  (1) 그래프에 수평선 추가하기 (adding horizontal lines)

  (2) 그래프에 수직선 추가하기 (adding vertical lines) 

 

하는 3가지 방법을 소개하겠습니다. 

 

예제로 사용할 샘플 데이터셋을 정규분포로부터 난수를 생성해서 100개 샘플을 추출하고, 점 그래프를 그려보겠습니다. 

이 기본 점 그래프에 수평선과 수직선을 차례대로 추가해보겠습니다. 

 

import numpy as np
import matplotlib.pyplot as plt

## generating random numbers
np.random.seed(1004)
x = np.random.normal(0, 1, 100)

## plotting the original data
plt.figure(figsize = (10, 6))
plt.plot(x, linestyle='none', marker='o', color='gray')
plt.show()

 

 

 (1) 그래프에 수평선 추가하기 (adding horizontal lines)

 

(a) plt.axhline(y, xmin, xmax) : 축을 따라서 수평선을 추가, xmin 과 xmax 는 0~1 사이의 값을 가짐

(b) plt.hlines(y, xmin, xmax) : xmin ~ xmax 까지 각 y 값의 수평선을 추가

(c) plt.plot((x1, x2), (y1, y2)) : (x1, x2), (y1, y2) 좌표를 연결하는 선 추가

 

(a) 번의 plt.axhline() 은 y축에서 부터 수평선이 시작하고, xmin~xmax 로 0~1 사이의 비율 값을 가지는 반면에, (b)번의 plt.hlines() 는 xmin 값 위치에서 부터 수평선이 시작하고, xmin~xmax 값으로 좌표값을 받는다는 차이점이 있습니다. 

(c) 번의 plt.plot() 은 단지 수평선, 수직선 뿐만이 아니라 범용적으로 두 좌표를 연결하는 선을 추가할 수 있습니다. 

 

plt.figure(figsize = (10, 6))
plt.plot(x, linestyle='none', marker='o', color='gray')
plt.title("Plot with Horizontal Lines", fontsize=16)

## (1) adding a horizontal line across the axis
## xmin and xmax should be b/w 0 and 1
plt.axhline(y=3, xmin=0, xmax=1, color='blue', linestyle='solid')
plt.axhline(y=2, xmin=0.1, xmax=0.9, color='blue', linestyle='dashed')

## (2) adding a horizontal line at each y from xmin to xmax
plt.hlines(y=0, xmin=0, xmax=50, color='red', linestyle='dotted')

## (3) adding a horizontal line using (x1, x2), (y1, y2) coordinates
plt.plot((50, 100), (-2, -2), color='black', linestyle='dashdot')

plt.show()

 

horizontal lines using matplotlib

 

 

(2) 그래프에 수직선 추가하기 (adding vertical lines) 

 

(a) plt.axvline(x, ymin, ymax) : 축을 따라서 수직선을 추가, ymin 과 ymax 는 0~1 사이의 값을 가짐

(b) plt.vlines(x, ymin, ymax) : ymin ~ ymax 까지 각 x 값의 수평선을 추가

(c) plt.plot((x1, x2), (y1, y2)) : (x1, x2), (y1, y2) 좌표를 연결하는 선 추가

 

(a) 번의 plt.axvline() 은 x축에서 부터 수평선이 시작하고, ymin~ymax 로 0~1 사이의 비율 값을 가지는 반면에, (b)번의 plt.vlines() 는 ymin 값 위치에서 부터 수평선이 시작하고, ymin~ymax 값으로 좌표값을 받는다는 차이점이 있습니다. 

(c) 번의 plt.plot() 은 단지 수평선, 수직선 뿐만이 아니라 범용적으로 두 좌표를 연결하는 선을 추가할 수 있습니다. 

 

plt.figure(figsize = (10, 6))
plt.plot(x, linestyle='none', marker='o', color='gray')
plt.title("Plot with vertical Lines", fontsize=16)

## (1) adding a vertical line across the axis
## ymin and ymax should be b/w 0 and 1
plt.axvline(x=0, ymin=0, ymax=1, color='blue', linestyle='solid')
plt.axvline(x=10, ymin=0.1, ymax=0.9, color='blue', linestyle='dashed')

## (2) adding a vertical line at each y from xmin to xmax
plt.vlines(x=50, ymin=0, ymax=3, color='red', linestyle='dotted')

## (3) adding a vertical line using (x1, x2), (y1, y2) coordinates
plt.plot((100, 100), (0, -3), color='black', linestyle='dashdot')

plt.show()

 

vertical lines using matplotlib

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 matplotlib 모듈을 사용하여, X축의 값은 동일하지만 Y축의 값은 척도가 다르고 값이 서로 크게 차이가 나는 2개의 Y값 데이터에 대해서 이중축 그래프 (plot with 2 axes for a dataset with different scales)를 그리는 방법을 소개하겠습니다. 

 

먼저 간단한 예제 데이터셋을 만들어보겠습니다. 

 

  * x 축은 2021-10-01 ~ 2021-10-10 일까지의 10개 날짜로 만든 index 값을 동일하게 사용하겠습니다. 

  * y1 값은 0~9 까지 정수에 표준정규분포 Z~N(0, 1) 로 부터 생성한 난수를 더하여 만들었습니다. 

  * y2 값은 정수 0~9에 지수를 취하여 만들었습니다. 

 

## importing modules
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


## generating sample dataset

np.random.seed(123) # for reproducibility
idx = pd.date_range("10 1 2021", 
                     periods=10, 
                     freq="d", 
                     name="Date")

y1 = np.arange(10) + np.random.normal(0, 1, 10)
y2 = np.exp(range(10))

df = pd.DataFrame({'y1': y1, 
                   'y2': y2}, 
                  index = idx)


print(df)
#                   y1           y2
# Date                             
# 2021-10-01 -1.085631     1.000000
# 2021-10-02  1.997345     2.718282
# 2021-10-03  2.282978     7.389056
# 2021-10-04  1.493705    20.085537
# 2021-10-05  3.421400    54.598150
# 2021-10-06  6.651437   148.413159
# 2021-10-07  3.573321   403.428793
# 2021-10-08  6.571087  1096.633158
# 2021-10-09  9.265936  2980.957987
# 2021-10-10  8.133260  8103.083928

 

 

먼저, 스케일이 다른 2개의 y값을 1개의 축을 사용하여 그렸을 때 문제점을 살펴보고, 

다음으로 이를 해결하기 위한 방법 중의 하나로서 matplotlib을 사용해 2중축 그래프를 그려보겠습니다. 

 

(* 참고로, 2중축 그래프 외에 서로 다른 척도(scale)의 두개 변수의 값을 표준화(standardization, scaling) 하여 두 변수의 척도를 비교할 수 있도록 변환해준 후에 하나의 축에 두 변수를 그리는 방법도 있습니다.)

 

 

(1)  스케일이 다른 2개의 y값 변수를 1중축 그래프에 그렸을 때 문제점

==> 스케일이 작은 쪽의 y1 값이 스케일이 큰 쪽의 y2 값에 압도되어 y1 값의 패턴을 파악할 수 없음. (스케일이 작은 y1값의 시각화가 의미 없음)

 

## scale이 다른 데이터를 1개의 y축만을 사용해 그린 그래프
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot() 

ax.plot(df.index, df.y1, marker='s', color='blue')
ax.plot(df.index, df.y2, marker='o', color='red')

plt.title('Plot with 1 Axis', fontsize=16)
plt.xlabel('Date', fontsize=14)
plt.ylabel('Value', fontsize=14)
plt.legend(['y1', 'y2'], fontsize=12, loc='best')
plt.show()

plot with 1 axis for a dataset with the different scales

 

 

(2) 스케일이 다른 2개의 변수에 대해 2중축 그래프 그렸을 때

==> 각 y1, y2 변수별 스케일에 맞게 적절하게 Y축이 조정이 되어 두 변수 값의 패턴을 파악하기가 쉬움

 

이때, 가독성을 높이기 위해서 각 Y축의 색깔, Y축 tick의 색깔과 그래프의 색깔을 동일하게 지정해주었습니다. (color 옵션 사용)

 

## plot with 2 different axes for a dataset with different scales
# left side
fig, ax1 = plt.subplots()
color_1 = 'tab:blue'
ax1.set_title('Plot with 2 Axes for a dataset with different scales', fontsize=16)
ax1.set_xlabel('Date')
ax1.set_ylabel('Y1 value (blue)', fontsize=14, color=color_1)
ax1.plot(df.index, df.y1, marker='s', color=color_1)
ax1.tick_params(axis='y', labelcolor=color_1)

# right side with different scale
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color_2 = 'tab:red'
ax2.set_ylabel('Y2 value (red)', fontsize=14, color=color_2)
ax2.plot(df.index, df.y2, marker='o', color=color_2)
ax2.tick_params(axis='y', labelcolor=color_2)

fig.tight_layout()
plt.show()

plot with 2 axes for a dataset with different scales

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터과학자 되세요!   :-)

 

728x90
반응형
Posted by Rfriend
,