1. 데이터 전처리

- 특정 분석에 적합하게 데이터를 가공하는 작업

 

- 완결성: 필수로 기입되어 있어야 하는 데이터는 모두 입력되어야 한다.

- 유일성: 동일한 데이터가 불필요하게 중복되어 있으면 안된다.

- 통일성: 데이터가 모두 동일한 형식으로 입력되어야 한다.

 

2. 주의해야 하는 점

- 잡음 Noise: 측정 과정에서 무작위로 발생하여 측정값의 에러를 발생시키는 것. 이상치와 달리 예측하기 어려움

- 아티펙트 Digital Artifact: 어떤 기술적인 요인으로 인해 반복적으로 발생하는 왜곡이나 에러

- 정밀도 Precision: 동일한 대상을 반복적으로 측정했을 때, 각 결과의 친밀성을 나타내는 것. 측정 결과의 표준편차로 표현 가능

- 편향 bias: 측정 장비에 포함된 시스템적인 변동 (ex. 영점 조절이 되지 않은 체중계)

- 정확도 Accuracy: 평향에 영향 받음. 유효 숫자의 사용에 있어서 중요함

- 이상치 Outlier: 대부분의 데이터와 다른 특성을 보이너가, 특정 속성의 값이 다른 개체들과 달리 유별난 값을 가지는 데이터. 잡음과 다름

- 결측치 Missing Value: 값이 표기되지 않은 값

- 모순, 불일치 Inconsistent Value: 동일한 개체에 대한 측정 데이터가 다르게 나타나는 경우

- 중복 Duplicated data: 중복된 데이터 사이에 속성의 차이나, 값의 불일치가 발생하면 문제가 됨

 

3. 전처리 순서

1) 데이터 수집: 분석이나 학습에 필요한 데이터를 수집하는 작업

- 데이터 분석 및 모델 생성의 첫 과정

- 목적에 맞는 정보 수집을 위해 문제 정의 필요

 

2) 데이터 정제: 비어있는 데이터나 잡음, 모순된 데이터를 정합성이 맞도록 교정하는 작업

- 데이터를 활용할 수 있도록 만드는 과정

- 컴퓨터가 읽을 수 없는 요소의 제거 및 숫자나 날짜 등의 형식에 대해 일관성 유지

- 누락값, 불일치 값, 오류 값 수정

 

3) 데이터 통합: 여러 개의 데이터 베이스, 데이터 집합 또는 파일을 통합하는 작업

- 서로 다른 데이터 세트가 호환이 가능하도록 통합

- 같은 객체, 같은 단위나 좌표로 통합

 

4) 데이터 축소: 샘플링, 차원 축소, 특징 선택 및 추출을 통해 데이터 크기를 줄이는 작업

- 대용량 데이터에 대한 복잡한 데이터 분석은 실행하기 어렵거나 불가능한 경우가 많음

 

5) 데이터 변환: 데이터를 정규화, 이산화 또는 집계를 통해 변환하는 작업

 

4. 데이터 전처리 기법

- 집계 Aggregation

- 샘플링 Sampling

- 차원 축소 Dimensionality Reduction

- 특징 선택 Feature Subset Selection

- 특징 생성 Feature Creation

- 이산화와 이진화 Discretization and Binarization

- 속성 변화 Attribute Transformation

 

5. 전처리 전 데이터 확인

- shape: 데이터 크기 출력

- head(): 데이터 상위 5개 행 출력

- tail(): 데이터 하위 5개 행 출력

- info(): 데이터 전반적인 정보 제공 (행/열 크기, 컬럼명, 컬럼을 구성하는 값의 자료형 등)

- describe(): 데이터 컬럼별 요약 통계량 제공

 

6. 결측치 처리

- NA : 값이 표기되지 않은 값. 결측치

- 제거, 대치, 예측모델로 처리

 

1) 전체 결측치 확인

df.isnull()
pd.isnull(df)

# 결측치일 때 true 반환
# isnull -> notnull : 결측치일 때 false 반환

 

2) 인덱싱 후 결측치 개수 확인하기

df['col'].isnull()

 

3) 결측치 만들기

df.ix[[row1, row2], ['col']] = None

 

4) 전체 결측치 개수 확인

df.isnull().sum()
df.isnull().value_counts()
df.isnull().sum(1)

 

5-1) 결측치 제거

- dropna() : 판다스에서 누락 데이터를 제거하는 함수

- 목록삭제 : 결측치가 있는 행/열은 전부 삭제

df = df.dropna()           # default, 행 제거
df = df.dropna(axis = 1)   # 열 제거

 

- 단일값 삭제 : 행/열 자체가 결측치일 때, 혹은 조건부 삭제

df = df.dropna(how = 'all')   
df = df.dropna(thresh = 1)     

df = df.dropna(subset=['col1', 'col2', 'col3'], how = 'all')  # 모두 결측치일 때 해당 행 삭제
df = df.dropna(subset=['col1', 'col2', 'col3'], thresh = 2)   # 특정 열에 2개 초과의 결측치가 있을 때 해당 행 삭제

 

5-2) 대치

- 단순 대치: 중앙값, 최빈값, 0, 분위수, 주변값, 예측값 등으로 결측치 대치

- 다중 대치: 단순 대치법을 여러번! (대치 - 분석 - 결합)

 

- 판다스에서 결측치 대치하는 함수들

fillna()

# 전체 결측치를 특정 단일값으로 대치
df.fillna(0)  

# 특정 열에 결측치가 있을 경우 다른 값으로 대치
df['col'] = df['col'].fillna(0)
df['col'] = df['col'].fillna(df['col'].mean())

# 결측치 바로 이후 행 값으로 채우기
df.fillna(method='bfill')

# 결측치 바로 이전 행 값으로 채우기
df.fillna(method='pad')
replace()

# 결측치 값 0으로 채우기
df.replace(to_replace = np.nan, value = 0)
interpolate()

# 인덱스를 무시하고, 값을 선형적으로 같은 간격으로 처리
df.interpolate(method = 'linear', limit_direction = 'forward')

 

5-3) 예측 모델

- 결측값을 제외한 데이터로부터 모델을 훈련하고 추정값을 계산하고 결측치 대체

- K-NN, 가중 K-NN, 로지스틱 회귀, SVM, 랜덤 포레스트 방식 등

 

7. 중복 데이터 처리

- 중복은 언제든지 발생할 수 있지만, 중복 데이터 사이에 속성의 차이나 값의 불일치가 발생한 경우, 처리해야 함

- 두 개체를 합치거나 응용에 적합한 속성을 가진 데이터를 선택하는 등

# 중복 데이터 확인
df.duplicated(['col'])

# 중복 데이터 삭제
drop_duplicates()

# 해당 열의 첫 행을 기준으로 중복 여부 판단 후, 중복되는 나머지 행 삭제
drop_duplicated(['col'])

df.drop_duplicates(keep = )
    subset = None           # default, 특정 열 지정 X, 모든 열에 대해 작업 수행
    keep = 'first'          # 가장 처음에 나온 데이터만 남김
    keep = 'last'           # 가장 마지막에 나온 데이터만 남김
    keep = False            # 중복된 어떤 데이터도 남기지 않음

 

8. 불균형 데이터 처리

- 분류를 목적으로 하는 데이터 셋에 클래스 라벨의 비율이 불균형한 경우

- 각 클래스에 속한 데이터 개수 차이가 큰 데이터

- 정상 범주의 관측치 수와 이상 범주의 관측치 수가 현저히 차이나는 데이터

- 이상 데이터를 정확히 찾아내지 못할 수 있음

 

8-1) Under Sampling

- 다수 범주의 데이터를 소수 범주의 데이터 수에 맞게 줄이는 샘플링 방식

- Random Undersampling, Tomek's Link, CNN

 

8-2) Over Sampling

- 소수 범주의 데이터를 다수 범주의 데이터 수에 맞게 늘리는 샘플링 방식

- Random Oversampling

- ADASYN, SMOTE

 

9. 이상치 탐지 기법

1) z-score

- z = (x - μ) / σ

- 변수가 정규분포 따른다고 가정, 각 특성값이 평균에서 표준편차의 몇 배만큼 떨어져 있는지 나타냄

- z-score가 임계치보다 크거나 작은 관측치를 이상치라고 규정

- 임계치 조정함으로써 기준 조정

def z_score_outlier(df, col, thres = 3):
    z_score = (df[col] - df[col].mean()) / df[col].std()
    return df[(z_score > thres) | (z_score < -thres)]

 

2) IQR

- IQR = Q3 - Q1

- Q3 + 1.5*IQR 이상이거나, Q1 - 1.5*IQR 이하인 경우 이상치라고 규정

- 1.5 대신 다른 값 이용해 기준 조정 가능

def IQR_outlier(df, col, scale = 1.5):
    Q1 = df[col].quantile(0.25)
    Q3 = df[col].quantile(0.75)
    IQR = Q3 - Q1
    lower_limit = Q1 - scale * IQR
    upper_limit = Q3 + scale * IQR
    
    return df[(df[col] > upper_limit) | df[col] < lower_limit)]

 

3) DBSCAN

- 밀도 기반 군집화 알고리즘

- 하이퍼 파라미터: eps(반경, default=0.5), min_samples(core point가 되기 위한 최소한의 이웃 수, default=5)

# 이상치 탐지
from sklearn.cluster import DBSCAN

model = DBSCAN(eps = .3, min_samples = 10)
pred = model.fit_predict(abalone)

# 이상치 개수
(pred == -1).sum()

 

10. 레이블 인코딩

- 문자열 카테고리 피처를 코드형 숫자 값으로 변환하는 것

# pandas
df.factorize()

# scikit-learn
LabelEncoder()
encoder.fit_transform() # 학습, 변환 한번에

 

11. 원핫 인코딩

- 피처값의 유형에 따라 새로운 피처를 생성해 고유값에 해당하는 컬럼에만 1 표시, 나머지 컬럼에는 0을 표시하는 방식

- 숫자의 크기 차이를 만드는 레이블 인코딩의 단점 보완

 

12. Feature Scaling

- 서로 다른 변수 값의 범위를 일정한 수준으로 맞추는 작업

- 변수 값의 범위 또는 단위가 달라서 발생 가능한 문제 예방

- 머신러닝 모델이 특정 데이터의 bias 갖는 것 방지

 

1) 표준화 Standardization

- 서로 다른 범위의 변수들을 평균이 0이고 분산이 1인 가우시안 정규분포를 가진 값으로 변환

- ScandardScaler()

 

2) 정규화 Normalization

- 변수값들을 모두 0과 1 사이의 값으로 변환하는 방식

- MinMaxScaler()

'ML & DL' 카테고리의 다른 글

[BITAmin] 선형 회귀  (0) 2023.01.23
[BITAmin] K-최근접 이웃 알고리즘  (2) 2023.01.23

[수업출처] 숙명여자대학교 소프트웨어학부 유석종 교수님 자료구조 수업

 

1. Python

- Interpreter

- 객체 지향 Object-oriented

- 자료형 미리 선언하지 않음 Dynamic typing

- Sequence, collection data type

- 값 저장X, 참조 방식 Literal reference

- 동적 할당 Dynamic allocation

 

- 파이썬의 모든 변수 → 포인터

- 값에 대한 참조 변수 (값이 저장되어 있는 곳의 주소 참조)

- a, b = b, a : swap

 

2. Data type

1) Immutable data type 불변자료형

- int, float, string

- 값 변경 불가능 → 값 변경이 아니고 새로운 literal 참조하는 것

- 매개변수 전달시 값 변경 불가능

 

2) Mutable data type 가변자료형

- list, dictionary

- 원소 값 변경, 추가 가능

- 리스트는 각 원소 값에 대한 참조들의 모음

- 매개변수 전달시 인수 값 변경될 수 있음

- 가변자료형: '참조변수'를 바꿀 수 있다는 것

 

3. Parameter Passing

- call by value

- call by reference

- python → call by object reference 객체 참조 전달

 

def f(a):
    a = 3
    print(a)
    return(a)
   
b = 2
f(b)         # 3
print(b)     # 2
b = f(b)     # 3
print(b)     # 3

return을 받으려면 변수에 입력해야함

 

4. Alias 별명

- 두 변수가 하나의 객체를 동시에 참조하여 공유하는 것

A = [[2, 3, 4], 5]
B = A
B[0][1] = 'f'
print(A, id(A))   # [[2, 'f', 4], 5] 1860139826944
print(B, id(B))   # [[2, 'f', 4], 5] 1860139826944

 

5. Shallow copy 얕은 복사

- 변수는 복사하여 새로 생성하지만 참조하는 리터럴은 공유하는 복사 방법

- copy 모듈의 copy() 함수로 실행

- 첫번째 level 객체만 복사

 

import copy

A = [[2, 3, 4], 5]
B = copy.copy(A)    # B = list(A)
B[0][1] = 'f'
B[1] = 7
print(A, id(A), id(A[0]), id(A[0][1]))  # [[2, 'f', 4], 5] 1860140611008 1860140611584 1860137884464
print(B, id(B), id(B[0]), id(B[0][1]))  # [[2, 'f', 4], 7] 1860140611904 1860140611584 1860137884464

- list A를 변수 B에 얕은 복사

- list B의 원소들은 새롭게 생성되지만, 원소들이 참조하는 객체는 원본과 동일하며 새로 생성되지 않음

- list A 와 B의 주소는 다르지만 리스트 원소가 참조하는 객체는 동일

 

6. Deep copy 깊은 복사

- 변수와 대상 객체(리터럴)를 모두 복제하여 생성

- 완전히 독립됨

 

import copy
A = [[2, 3, 4], 5]
B = copy.deepcopy(A)
B[0][1] = 'f'
B[1] = 7
print(A, id(A), id(A[0]), id(A[0][1]), id(A[1])
# [[2, 3, 4], 5] 1482518103488 1482518184832 1482509412720 1482509412784

print(B, id(B), id(B[0]), id(B[0][1]), id(B[1])
# [[2, 3, 4], 5] 14825181072000 1482518204544 1482509753136 1482509412848

- 변수 A와 B, 리스트 원소, 원소가 참조하는 객체의 주소가 모두 다름

- 리스트 원소가 참조하는 객체도 복제되기 때문에 B의 원소 값을 수정해도 리스트 A가 참조하는 객체에는 변화가 없음

 

7. List

- 원소의 자료형 같을 필요 없음

- 파이썬은 정적 배열 지원하지 않음

- 사이즈는 고정되어 있지 않음 dynamic

- referential array 리스트 원소는 값에 대한 참조 변수

A = [2, 3, 4]
A.append(7)
A[1] = 9

 

 

1) 리스트 생성

    - 리스트 복합 표현

    - range(), list() 함수 사용

    - 빈 리스트 선언 후 원소 추가

# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

num1 = [i for i in range(10)]
num2 = list(range(10))
num3 = []
for i in range(10):
    num3 = num3 + [i]  # num3.append(i)

 

2) 주사위 던지기 예제

import random

def throw_dice(count, n):
    for i in range(n):
        eye = random.randint(1, 6)
        count[eye-1] += 1
    
def calc_ratio(count):
    ratio = []
    total = sum(count)
    for i in range(6):
        ratio.append(count[i]/total)
    return ratio

def print_percent(num):
    print('[', end = '')
    for i in num:
        print('%4.2f %i, end = ' ')
    print(']')
dice = [0]*6
for n in [10, 100, 1000]:
    print('Times = ', n)
    throw_dice(dice, n)
    print(dice)
    ratio = calc_ratio(dice)
    print_percent(ratio)
    print()
Times = 10
[2, 1, 2, 1, 2, 2]
[0.20, 0.10, 0.20, 0.10, 0.20, 0.20]

Times = 100
[17, 14, 24, 28, 16, 21]
[0.15, 0.13, 0.22, 0.16, 0.15, 0.19]

Times = 1000
[197, 185, 176, 162, 188, 202]
[0.18, 0.17, 0.16, 0.15, 0.17, 0.18]

 

3) 2차원 배열 생성

scores = [[75, 90, 85], [60, 100, 75], [90, 70, 80]]
print('scores', scores)
for score in scores:
    for num in score:
        print(num, end = ' ')
    print()
print()

print('scores[0][1]', scores[0][1])
print('scores[2]', scores[2])
print('scores[2][2]', scores[2][2])
scores [[75, 90, 85], [60, 100, 75], [90, 70, 80]]
75 80 85
60 100 75
90 70 80

scores[0][1] 90
scores[2] [90, 70, 80]
scores[2][2] 80

 

4) 볼링 점수 계산 예제

score1 = [(8,0), ((4,3), (8,2), (4,6), (2,6), \
         (10,0), (9,0), (10,0), (8,2), (10,0), (10,10)]
score2 = [(10,0), (10,0), (10,0), (10,0), (10,0), \
          (10,0), (10,0), (10,0), (10,0), (10,0), (10, 10)]

score_list = [score1, score2]      # 첫번째, 두번째 게임
for score in score_list:
    i = total = 0
    frame = []                     # (프레임 점수, 결과)
    for first, second in score:    # 1회, 2회 쓰러뜨린 핀의 수
        f_total = first + second
        next_first, next_second = score[i+1]
        if first == 10:
            result = 'STRIKE'
            f_total += next_first + next_second
            # 1~9 프레임에서 더블을 친 경우
            if i != 9 and next_first == 10:
                next_next_first, next_next_second = score[i+2]
                f_total += next_next_first
        elif (first + second) == 10:
            result == 'SPARE'
            f_total += next_first
        else: result = 'NONE'
            
        total += f_total
        frame.append((f_total, result))
        i += 1
        if i == 10: break
    print(frame)
    print('Total = ', total)
    print()
[(8, 'NONE'), (7, 'NONE), (14, 'SPARE'), (12, 'SPARE'), (8, 'NONE'), (19, 'SPARE'), (9, 'NONE'), (20, 'STRIKE'), (20, 'SPARE'), (30, 'STRIKE')]
Total = 147

[(30, 'STRIKE'), (30, 'STRIKE'), (30, 'STRIKE'), (30, 'STRIKE'), (30, 'STRIKE'), (30, 'STRIKE'), (30, 'STRIKE'), (30, 'STRIKE'), (30, 'STRIKE'), (30, 'STRIKE')]
Total = 300

 

8. Set and Dictionary

1) Set

- 수학의 집합과 유사한 자료구조로 집합 연산자 지원

- 원소의 나열 순서가 없으며 중복 허용하지 않음

- s1 = set([1, 2, 3])

- s2 = {2, 3, 4}

- 연산자

    - membership( in ), 합집합 union( | ), 차집합 difference( - )

    - add()

    - update( {  } )

    - remove()

 

2) Dictionary

- key & value의 쌍으로 구성된 자료형

- 키를 통해 원소에 접근

- items() : 각 항목 튜플로 반환

- keys() : 키만을 추출하여 리스트로 반환

- values() : 값만을 추출하여 리스트로 반환

 

- 단어 출현 빈도 예제

sentence = sentence.lower()    # 소문자로 변환
words = sentence.split()       # 단어 단위로 분리
dic = {}
for word in words:
    if word not in dic:
        dic[word] = 0
    dic [word] += 1
print('# of different words =', len(dic))
n = 0
for word, count in dic.items():
    print(word, '(%d)' %count, end = ' ')
    n += 1
    if n % 3 == 0: print()
# sentence = 'Van Rossum was born and raised in the Netherlands, where he received a master's degree in mathematics and computer science from the University of Amsterdam in 1982. In December 1989, Van Rossum had been looking for a hobby programming project that would keep him occupied during the week around Christmas as his office was closed when he decided to write an interpreter for a new scripting language he had been thinking about lately, a descendant of ABC that would appeal to Unix / C hackers. '

# of different words = 66
van (2) rossum (2) was (2) born (1) and (2) raised (1) ...

 

9. Polynomial ADT 다항식 객체 (추상자료형)

- polynomial A(x) = 3x^20 + 2x^5 + 4

- x: variable, a: 계수 coefficient, e: 지수 exponent

- 튜플로 저장해야할 것: coefficient, exponent

 

# 다항식 덧셈

def padd(a, b, d):
    while a and b:
        coef1, exp1 = a[0]
        coef2, exp2 = b[0]
        if exp1 > exp2:
            d.append(a.pop(0))
        elif exp1 < exp2:
            d.append(b.pop(0))
        else:
            if coef1 + coef2 == 0:
                a.pop(0)
                b.pop(0)
            else:
                d.append((coef1 + coef2, exp1))
                a.pop(0)
                b.pop(0)
    for coef, exp in a:
        d.append((coef, exp))
    for coef, exp in b:
        d.append((coef, exp))
a = [(5, 12), (-6, 8), (13, 3)]
b = [(10, 15), (4, 8), (9, 0)]
d = []

padd(a, b, d)
print(d)

# [(10, 15), (5, 12), (-2, 8), (13, 3), (9, 0)]

 

10. Sparse Matrix 희소 행렬

- 2D array: matrix[m][n] → 공간복잡도 S(m, n) = m*n

- Sparse matrix A[m, n] : 유효한 값은 거의 없고 나머지는 0인 행렬

- 2D array에 저장하기에 메모리 공간이 아까운 경우 → 0이 아닌 값 저장 (행, 열, 값)

 

- a[0].row: 행렬의 행 수

- a[0].col: 행렬의 열 수

- a[0].value: 0이 아닌 값의 개수

- a[1]부터 0이 아닌 값의 행, 열, 값 저장 (left-right, top-bottom)

 

class SparseMatrix:
    def __init__(self):
        self.matrix = [[15, 0, 0, 22, 0, 15], \
                       [0, 11, 3, 0, 0, 0], \
                       [0, 0, 0, -6, 0, 0], \
                       [0, 0, 0, 0, 0, 0], \
                       [91, 0, 0, 0, 0, 0], \
                       [0, 0, 28, 0, 0, 0]]
        self.sparse_list = []

def toTuple(self):
    row = count = 0
    for rows in self.matrix:
        col = 0
        for value in rows:
            if value != 0:
                count += 1
                self.sparse_list.append((row, col, value))
            count += 1
        row += 1
    height = len(self.matrix)
    width = len(self.matrix[0])
    self.sparse_list.insert(0, (height, width, count))
s = SparseMatrix()
s.toTuple()
print(s.sparse_list)

# [(6, 6, 8), (0, 0, 15), (0, 3, 22), (0, 5, 15), 
# (1, 1, 11), (1, 2, 3), (2, 3, -6), (4, 0, 91), (5, 2, 28)]

 

'Software > Data Structures' 카테고리의 다른 글

[Python] 2장 연습문제  (0) 2022.04.16
[Python] 1. Introduction  (0) 2022.03.08
[자료구조] 탐색  (0) 2021.04.20
[자료구조] 연결 리스트  (0) 2021.04.20
[자료구조] 선형 자료구조  (0) 2021.04.18

[수업출처] 숙명여자대학교 소프트웨어학부 유석종 교수님 수업 '자료구조'

 

1. Data Structures

- 문제 해결을 위한 대량의 데이터를 효율적으로 처리하고 조직하는 방법 또는 구조

 

- Linear DS: list, stack, queue, deque

- Non-Linear DS: tree, graph

- Advanced DS: BST, Weight networks

- Searching and Sorting algorithms

 

2. Algorithm

- 특정 작업을 수행하기 위한 명령어들의 유한한 집합

- 조건

    - Input: 명시적 입력이 필요하지 않다.

    - Output: 적어도 하나의 출력값이 있어야 한다.

    - Definiteness: 구체적이고 모호하지 않은 명령어들의 집합이어야 한다.

    - Finiteness: 유한한 단계를 거친 후 끝나야 한다.

    - Effectiveness: 쓸모 있어야하고, 효율적이어야 한다.

- 표현 방법

    - Pseudo code, Flow chart

    - 자연어, 프로그래밍 언어

 

3. System Life Cycle

- Requirement 요구사항 명세

    - 프로젝트의 목적을 정의하는 일련의 설계 명세서

    - 사용자, 플랫폼, 함수, 인터페이스 등

- Analysis 분석

    - 큰 프로젝트를 작은 모듈들로 나누는 과정

    - Divide and conquer 전략

    - Top-down approach

        - 주 목적에서 구성요소로 가는 계층적 접근

        - program -> subroutines -> instructions

    - Bottom-up approach

        - 구성요소가 모여 전체 시스템이 되는 방법

- Design 설계

    - 각 모듈에 대한 객체와 함수 정의

- Implementation 구현

    - 객체 및 알고리즘 코드 구현

- Verification 검증

    - 알고리즘의 정확성 증명

    - program test: black-box test, white-box test

 

4. Fibonacci Numbers

# fb는 리스트

def fibo(fb):
    a = 0
    b = 1
    fb.append(a)
    fb.append(b)
    while True:
    	a, b = b, a+b
        if b < n: break
        fb.append(b)

피보나치 수열을 구하는 함수이다.

리스트에 우선 0과 1을 넣고, b가 n이 되기 전까지 a = b, b = a+b로 대입하여 b, 즉 a+b를 리스트에 추가한다.

 

n = 100000
fb = []
fibo(fb)
print(fb)
print("# of fibos", len(fb))

 

5. Prime Numbers

- 소수: 1보다 큰 자연수 중 1과 자기자신 이외의 수로 나누어 떨어지지 않는 수

 

def find_prime(num):
    for i in range(2, num+1):
        prime = 1
        for j in range(2, i):
            if i % j == 0:
                prime = 0
                break
        if prime: prime_list.append(i)

2부터 num까지의 수를 각각 i로 둘 때, i를 2부터 i까지의 수로 나누어 본다. 

이 때, 나누어 떨어지면 소수가 아니기 때문에 prime = 0으로 둔다. 

i까지 전부 나눈 후  prime이 여전히 1이면 소수이기 때문에 prime_list에 추가한다. 

이 과정을 num까지 반복한다.

 

prime_list = []
max_num = 1000
find_prime(max_num)


for i in range(2, max_num):
    if i in prime_list: print('%3d' %i, end = '')
    else: print('_', end = '')
    if i % 50 == 0: print()
print()
print('# of prime numbers = ', len(prime_list))

 

6. Data Types

- 객체와 해당 객체에 작용하는 명령어들의 집합

 

- 내장 자료형

    - Basic type: char, int, float

    - Collection type: string, list, tuple, set, dictionary

 

- 추상 자료형

    - 객체의 자료형

 

7. Abstract Data Type (ADT) 추상 자료형

- 객체의 속성(attributes, properties, member variables)과 행동(methods, operations, member funtions)을 추상화하는 자료형

- 사용자가 필요에 의해 자료형을 직접 정의하는 것

- 객체지향 프로그래밍에서는 공통적인 행위를 하는 Class를 정의한다.

- ex) bank account, student, fraction

 

8. Fraction 분수

- 분자와 분모로 이루어져 있다.

- 기본적으로 분수는 내장되어있지 않기 때문에 객체로 정의해주어야 한다.

- 약분까지 해야 함 -> gcd(x, y)

 

class Fraction:
    def __init__(self, up, down):
        self.num = up
        self.den = down
    
    def __str__(self):
        return str(self.num) + '/' + str(self.den)
    
    def __add__(self, other):
        new num = self.num * other.den + self.den * other.num
        new_den = self.den * other.den
        common = self.gcd(new_num, new_den)
        return Fraction(new_num//common, new_den//common
        
    def multiply(self, other):
        new num = self.num * other.num
        new_den = self.den * other.den
        common = self.gcd(new_num, new_den)
        return Fraction(new_num//common, new_den//common)
        
    def gcd(self, a, b):
        while a % b != 0:
            a, b = b, a % b
        return b

def __init__(self, up, down)은 Fraction 클래스를 호출했을 때 두번째 매개변수 up을 self.num에, 세번째 매개변수 down을 self.den에 대입하는 메서드이다. 이때 self.num은 분자, self.den은 분모이다.

 

def __str__(self)는 print(Fraction(up, down)을 실행했을 때 호출되는 메서드이다. 분수형태로 출력해준다.

 

def __add__(self, other)는 덧셈 메서드이다. + 로 두 변수를 연결했을 때 자동으로 호출된다.

분자는 self의 분자 x other의 분모 + self의 분모 x other의 분자이고,

분모는 self의 분모 x other의 분모이다.

common은 덧셈 결과의 분자와 분모의 최대공약수로, 이후에 나올 gcd 메서드를 통해 구할 수 있다.

최종적으로 덧셈 결과의 분자와 분모를 최대공약수로 약분해서 결과를 도출한다.

 

def multiply(self, other)는 곱셈 메서드이다.

분자는 self의 분자와 other의 분자를 곱한 값이고,

분모는 self의 분모와 other의 분모를 곱한 값이다.

마찬가지로 gcd 메서드로 최대공약수를 구한 후 곱셈 결과를 약분해서 도출한다.

 

def gcd(self, a, b)는 분자와 분모의 최대공약수를 구하는 메서드이다.

gcd(a, b) = gcd(b, a%b) 공식을 이용하였다.

 

num1 = Fraction(1, 4)
num2 = Fraction(1, 2)
num3 = num1 + num2
print(num1, '+', num2, '=', num3)

num1 = Fraction(3, 4)
num2 = Fraction(2, 9)
num3 = num1.multiply(num2)
print(num1, '*' num2, '=', num3

 

9. Performance Analysis

- 이상적 기준 → 추상적

    - 고객의 요구사항을 만족하는지?

    - 잘 작동하는지?

    - 효율적으로 구현했는지?

- 현실적 기준 → 객관적 수치 도출

    - 공간복잡도: 실행할 때 메모리 공간을 얼마나 사용하는지

    - 시간복잡도: 연산 시간이 얼마나 소요되는지

 

10. 공간복잡도 Space Complexity

- Fixed space requirements(Sc) : 명령어, 단순 변수, 고정크기의 구조체 변수, 상수 등으로 컴파일 전에 알 수 있음

- Variable space requirements(Sv): 배열 함수 전달, 재귀호출 등으로 실행 중에 결정됨. Sv를 구해야 함

- S = Sc + Sv

 

def abc(a, b, c):
    return a + b * c  + (a + b - c) / (a + b) + 4.00

- input: 3개의 단순 변수

- output: 1개의 단순 변수

- 배열 전달, 재귀 X → 가변 요구공간 = 0

- 고정 요구공간은 있지만 관심있는 부분이 아님

 

def iSum(num):
    total = 0
    for item in num:
        total = total + num
    return total

- input: 배열(num)

- output: 단순 변수

- 공간복잡도는 배열 함수 전달 방법에 달려있음 (copy or 주소 전달)

 

- call by value 

    - 배열 요소들은 함수로 '복사'되어짐

    - 배열 크기에 비례하여 추가적인 메모리 공간이 요구됨

    - Sv = n

    - ex) Pascal

 

- call by reference 

    - 배열의 시작 주소만 함수로 전달됨

    - 추가적인 메모리 공간 요구되지 않음 

    - Sv = 0

    - ex) C, Python

 

- 재귀호출

def rsum(sum):
    if len(num):
        return rsum(num[:-1]) + num[-1]
    else:
        return 0
print(rsum([1, 2, 3, 4, 5])

- 함수가 호출될 때마다 현재 실행중인 문맥 모두 저장되어야 함

    - variables, return address, n times funcion calls (rsum(n-1), ..., rsum(0))

    - space complexity = n * (size(num) + return address)

 

11. 시간복잡도 Time complexity

- 알고리즘이 수행되는데 걸리는 시간

- T = compile time(Tc) + excution time(Te)

- 실행시간(Te)가 시간복잡도를 구할 때 사용되며, 실행 환경에 영향을 받음

→ 명령어 개수로 시간 측정, 명령어 별로 실행 시간 다른 것은 무시함

 

- Step count table

    - steps: 한 라인에 명령어가 몇 개인지 (함수 헤더, 변수 선언은 무시함)

    - frequency: 명령어가 몇 번 실행되는지 (for, while loops)

    - total steps = steps * frequency per line

 

12. Big-O Notation

- n이 매우 큰 경우 시간복잡도 함수를 점근적으로 근사한 것

- n이 증가할 경우 알고리즘을 실행하는데 소요되는 시간

- 알고리즘 성능 평가를 위한 표준

 

- T(n) = n² + 2n → O(n²)

 

- f(n) ≤ c * g(n) for all n ≥ n0 를 만족하는 정수 n0와 c가 존재하면 Big-O notation 사용 가능

- c * g(n)은 f(n)의 상한선 → c * g(n)보다는 시간이 적게 걸림

 

- ex) f(n) = 10n + 10,000 ∈ O(n)

    - f(n) ≤ 20n (=c) for all n ≥ 1000 (=n0)

 

13. Big-Omega Notation

- f(n) ≥ c * g(n) for all n ≥ n0 를 만족하는 정수 n0와 c가 존재하면 Big-Omega notation 사용 가능

- c * g(n)은 f(n)의 하한선

 

- ex) f(n) = 3n + 2 ∈ Ω(n)

    - f(n) ≥ 3n for n ≥ 1

 

14. Big-Theta Notation

- f(n) ∈ Ω(n) 이고 f(n) ∈ O(n)을 만족하는 c1, c2, n0가 존재하면 f(n) ∈ θ(g(n()) 존재

- c1 * g(n)  f(n)  c2 * g(n) for all n ≥ n0

 

- ex) f(n) = 3n + 2 ∈ θ(n)

    - 3n ≤ f(n) ≤ 4n for all n ≥ 2

 

15. Time complexity class

'Software > Data Structures' 카테고리의 다른 글

[Python] 2장 연습문제  (0) 2022.04.16
[Python] 2. Python Data Type  (0) 2022.04.16
[자료구조] 탐색  (0) 2021.04.20
[자료구조] 연결 리스트  (0) 2021.04.20
[자료구조] 선형 자료구조  (0) 2021.04.18

출처 https://www.boostcourse.org/ds214/lecture/102086?isDesc=false 

 

프로젝트로 배우는 데이터사이언스

부스트코스 무료 강의

www.boostcourse.org

 

1. 당뇨병 데이터셋

https://www.kaggle.com/uciml/pima-indians-diabetes-database

https://www.scikit-learn.org/stable/modules/generated/sklearn.datasets.load_diabetes.html#sklearn.datasets.load_diabetes

* pregnancies : 임신횟수
* glucose : 2시간 동안의 경구 포도당 내성 검사에서 혈장 포도당 농도
* bloodPressure : 이완기 혈압 (mm Hg)
* skinThickness : 삼두근 피부 주름 두께 (mm), 체지방을 추정하는데 사용되는 값
* insulin : 2시간 혈청 인슐린 (mu U / ml)
* BMI : 체질량 지수 (체중kg / 키 (m)^2)
* diabetesPedigreeFunction : 당뇨병 혈통 기능
* age : 나이
* outcome : 당뇨병인지 아닌지, 768개 중에 268개의 결과 클래스 변수 (0 / 1)는 1이고 나머지는 0이다. 

 

2. 라이브러리 import

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

%matplotlib inline

 

3. 데이터셋 로드

df = pd.read_csv("diabetes.csv")  #주피터노트북에서 실행 -> 경로 간단
df.shape

9개의 feature가 존재하고, 768개의 데이터가 있다.

 

df.head()

결측치가 없고 다 숫자로 되어있기 때문에 이 데이터셋은 전처리를 하지 않아도 괜찮다.

 

4. 학습, 예측 데이터셋 나누기

# 8:2 비율로 구하기 위해 전체 데이터 행에서 80% 위치에 해당되는 값을 구해서 split_count 변수에 담기

split_count = int(df.shape[0] * 0.8) 
split_count

# train, test로 슬라이싱을 통해 데이터 나눔

train = df[:split_count].copy()
train.shape

test = df[split_count:].copy()
test.shape

데이터셋 자체로 학습을 하고, 예측 후 정확도까지 알아보기 위해서 위에서부터 80%는 학습데이터, 나머지 20%는 예측데이터로 분리했다.

80% 지점이 614번째 데이터이기 때문에

학습데이터는 614개,

예측데이터는 154개이다.

 

5. 학습, 예측에 사용할 컬럼

# feature_names 라는 변수에 학습과 예측에 사용할 컬럼명을 가져옴
# 여러개를 가져왔기 때문에 list 형태로 가져옴

feature_names = train.columns[:-1].tolist()
feature_names

# label_name 에 예측할 컬럼의 이름 담음

label_name = train.columns[-1]
label_name

feature는 이렇게 9개가 있는데, 우리가 예측할 것은 마지막 컬럼인 outcome (당뇨병인지 아닌지) 이기 때문에 뒤에서 두번째 컬럼인 Age까지를 feature_names에 저장하고, 마지막 outcome을 label_name에 저장했다.

 

6. 학습, 예측 데이터셋 만들기

# 학습 세트 만들기 
# 행렬

X_train = train[feature_names]
print(X_train.shape)
X_train.head()

앞에서 나눈 80%의 학습데이터셋 train에 feature_names 컬럼을 적용하여 학습데이터셋 X_train 을 만들었다.

 

# 정답 값
# 벡터

y_train = train[label_name]
print(y_train.shape)
y_train.head()

마찬가지로 앞에서 나눈 80%의 학습데이터셋에 label_name 컬럼을 적용하여 학습할 때 정답을 맞추는 데이터셋 y_train을 만들었다.

 

# 예측에 사용할 데이터셋

X_test = test[feature_names]
print(X_test.shape)
X_test.head()

 

다음은 앞에서 나눈 20%의 예측데이터셋에 feature_names를 적용하여 학습한 알고리즘에 적용할 예측데이터셋 X_test를 만들었다.

# 예측의 정답값
# 실전에는 없지만, 실전 적용 전 정답을 알고있기 때문에 내가 만든 모델의 성능을 측정해봐야 함

y_test = test[label_name]
print(y_test.shape)
y_test.head()

마지막으로, 예측 모델의 성능을 측정하기 위한 예측 정답 데이터셋을 만들었다.

20% 예측 데이터셋에 label_name을 적용한 데이터셋으로, 실전 예측 문제에는 없지만 이 예제에서는 알고있는 값이기 때문에 모델의 성능을 측정할 때 사용할 것이다.

 

7. 머신러닝 알고리즘 가져오기

from sklearn.tree import DecisionTreeClassifier

model = DecisionTreeClassifier()
model

decision tree classifier 알고리즘을 사용할 것이기 때문에 이 알고리즘을 model 변수에 저장해준다.

 

8. 학습 (훈련)

model.fit(X_train, y_train)

아주 간단하다.

머신러닝 알고리즘.fit(훈련데이터, 정답데이터) 형식이다.

 

9. 예측

y_predict = model.predict(X_test)
y_predict[:5]

학습이 기출문제와 정답으로 공부를 하는 과정이었다면, 예측은 실전 문제를 푸는 과정이라고 볼 수 있다.

위에서 5개 데이터의 예측 결과를 보자면 이와 같다. 1은 당뇨병, 0은 당뇨병이 아니라는 것이다.

 

10. 트리 알고리즘 분석

from sklearn.tree import plot_tree

#plot_tree(model, 
#         feature_names = feature_names)  
# 글자로 나옴

# 시각화

plt.figure(figsize = (20, 20))

tree = plot_tree(model, 
                 feature_names = feature_names,
                filled = True,
                fontsize = 10)

 

# 피처의 중요도 표시 

model.feature_importances_

각 피처의 중요도를 알려준다.

 

# 피처의 중요도 시각화

sns.barplot(x = model.feature_importances_, y = feature_names)

seaborn을 활용해서 각 피처의 중요도를 시각화했다.

 

11. 정확도(Accuracy) 측정하기

# 실제값 - 예측값 -> 같은 값은 0으로 나옴
# 여기서 절대값을 씌운 값 = 1 인 값이 다르게 예측한 값임

diff_count = abs(y_test - y_predict).sum()
diff_count

# decision tree 예측할 때마다 다르게 예측할 수 있기 때문에 이 값은 다르게 나올 수 있음
# 항상 같게 하려면 model = DecisionTreeClassifier(random_state = 42)처럼 random_state 지정

우리는 예측 데이터셋의 정답도 알고있기 때문에 모델의 정확도를 측정할 수 있다.

outcome 값은 0 또는 1이기 때문에 실제값에서 예측값을 뺀 값의 절대값이 1인 값이 틀린 것이라고 볼 수 있다.

실제값에서 예측값을 뺀 값들의 합을 (틀린 예측의 수) diff_count 변수에 저장했다.

 

# 예측의 정확도 구하기 -> 100점 만점에 몇 점인지

(len(y_test) - diff_count) / len(y_test) * 100

예측데이터셋에서 틀린 예측의 수를 뺀 비율로 정확도를 구했더니 약 69%가 나왔다.

decision tree는 예측할 때마다 가지의 수 등의 원인으로 다르게 예측할 수 있다.

이를 고정하려면 알고리즘을 불러올 때 속성에서 random_state 값을 지정해주어야 한다.

 

# 위처럼 직접 구할 수도 있지만, 미리 구현된 알고리즘을 가져와 사용한다

from sklearn.metrics import accuracy_score

accuracy_score(y_test, y_predict) * 100

 

이건 사이킷런에 미리 구현된 알고리즘으로 정확도를 계산한 것이다. 위와 같은 값이다.

 

# model 의 score로 점수 계산 (정답값 y_test 알고있을 때)

model.score(X_test, y_test)

이건 model 의 score로 점수를 계산한 것이다. 정답값을 알고있을 때 사용하며, 위에서의 정확도와 같다.

출처 https://www.boostcourse.org/ds214/joinLectures/28155

 

프로젝트로 배우는 데이터사이언스

부스트코스 무료 강의

www.boostcourse.org

 

1. Scikit Learn

- 대표적인 파이썬 머신러닝 라이브러리

- classification, regression, clustering, dimensionality reduction, model selection, preprocessing 등 알고리즘

 

- supervised machine learning

clf = RandomForestClassifier()  # 머신러닝 모델 (트리모델)

clf.fit(X_train, y_train)  # 학습, x는 학습 데이터, y는 정답

 

y_pred = clf.predict(X_test)  # 예측

clf.score(X_test, y_test)  # 확인, 현실 문제에서는 y_test 알 수 없을 것, 하지만 임의로 데이터를 나누어 학습시켰다면 알 수 있음

 

- unsupervised transfomations

pca = PCA()  # 비지도학습 중 차원축소기법

pca.fit(X_train)

X_new = pca.transform(X_test)

 

- estimator.fit(X, [y])

estimator: 지정해줄 수 있는 알고리즘 모델

fit -> 학습 (비지도학습은 y 없음)

 

* estimator.predict -> classification, regression, clustering

* estimator.transform -> preprocessing, dimensionality reduction, feature seelction, feature exteaction

 

2. 데이터 검증

 

데이터에 알맞는 모델과 파라미터를 찾기 위해 훈련 데이터로 테스트를 진행한다.

 

1. 데이터를 training data와 test data로 구분한다.

2. cross-validation을 통해 여러 fold로 나눈다.

3. 나눠준 폴더의 개수만큼 훈련을 진행한다.

4. 예를 들어 5개의 폴더로 나누면 fold1부터 5까지 하나씩 test data로 놓고 나머지로 training 후 test data로 성능을 확인한다.

5. 각각의 fold에 있는 점수의 평균을 내서 가장 좋은 점수를 내는 모델과 파라미터를 찾는다.

 

clf = SVC(파라미터)

clf.fit(X_train, y_train)

 

3. grid searches

이러한 과정을 거쳐 알맞은 모델과 파라미터를 구한다.

 

4. 기본 예제

from sklearn imoprt tree
X = [[0, 0], [1, 1]]
y = [0, 1]

clf = tree.DecisionTreeClassifier()
clf.fit(X, y)

clf.predict([[2., 2.]])

clf.predict_proba([[2., 2.]])

 

결정 트리 분류 모델에 x = [0, 0] 일  때 y = 0이고, x = [1, 1] 일 때 y = 1인 훈련 데이터를 학습시켰다.

그리고 x = [2., 2.]일 때 y값을 예측했더니 1이 나왔다.

 

estimator.predict_proba 는 예측값을 비율로 출력하는 함수이다.

 

5. iris dataset 예제

from sklearn.datasets import load_iris
from sklearn import tree
import matplotlib.pyplot as plt

X, y = load_iris(return_X_y = True)
X, y              # decisiontree 알고리즘이 숫자만 읽을 수 있기 때문에 카테고리 데이터 -> 숫자화

사이킷런 라이브러리에 내장되어 있는 아이리스 데이터셋을 불러왔다.

원래는 문자로 된 범주형 데이터지만, decision tree 알고리즘이 숫자만 읽을 수 있기 때문에 숫자로 변환한 것이다.

 

clf = tree.DecisionTreeClassifier()
clf = clf.fit(X, y)

plt.figure(figsize = (20, 10))
t = tree.plot_tree(clf.fit(X, y), filled= True)

 

decision tree classifier 를 이용하여 데이터를 학습시킨 트리 모델이다.

이를 통해 중요한 변수가 무엇인지 등을 알 수 있다.

 

더 그래프를 보기좋게 그리기 위해서 graphviz 라이브러리를 활용할 수 있다. 하지만 설치가 조금 까다롭기 때문에 나중에 다시 해보려고 한다.

+ Recent posts