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

 

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

출처) 숙명여자대학교 소프트웨어학부 김주균 교수님 '운영체제' 수업 & [OS? Oh Yes!] , 220303

 

1. 운영체제

- 컴퓨터의 여러 응용 프로그램을 설치되게 해주고, 여러가지 장치를 효율적으로 작동하도록 하며, 사용자가 컴퓨터를 손쉽게 이용할 수       있도록 해주는 프로그램의 집단

- 하드웨어의 여러 부분을 건드리는 경우가 많기 때문에 하드웨어에 의존적인 경우가 많고, 따라서 이전에는 하드웨어 회사마다 자신만의 OS를 갖고 있었음

- 크게 사용자 인터페이스(User Interface)와 자원 관리(Resource Management)를 위한 프로그램의 집합으로 설명할 수 있음

 

2. 운영체제 변천사

    2-1. 수동식 계산기

    - 기원전 3000년 경 중국에서 발명된 주판이 발판이 됨

    - 존 네피어 봉, 파스칼의 톱니바퀴 계산기, 라이프니츠의 계산기 등

    - 이 당시에 운영체제는 존재하지 않았음

 

    2-2. 자동식 계산기

    - 배비지의 증기기관으로 작동되는 해석장치를 시작으로 홀러리스의 천공기 등이 있으며

    - 에이컨의 MARK Ⅰ은 23자리 숫자의 가감을 매초 3회, 곱셈은 3초에 1회씩 계산이 가능했음

    - 이 때에도 운영체제는 없었으며 소수의 기술자에 의해 수동으로 조작되었음

 

    2-2-1. 1세대 운영체제 (1940-1950)

    - 진공관 컴퓨터 시기

    - ABC(45개의 진공관) → ENIAC(최초의 컴퓨터) → EDSAC(폰 노이만 2진 부호 체계) → UNIVAC-Ⅰ → IBM 701 → IBM 305(자기 디스크 장치 채택) 등 

    - IBM 701에 와서 비로소 운영체제의 효시라 할 수 있는 기능 장착됨 → "일괄처리 시스템"

일괄처리 시스템 (Single-stream Batch Processing Systems)
- 다수 개의 프로그램을 읽어 저장해놓되, 한 번에 한 개씩의 프로그램을 실행시키는 방식
- 다음 작업을 처리할 때의 시간 절약

Batch
- 초창기: 작업이 차례대로 한 개씩 처리된다는 뜻. 한 개의 작업이 시작되면 그 작업이 끝날 때까지 다른 작업은 기다려야 함
- 이후: 일괄처리를 하되 다중 프로그래밍을 하는 Batch로 발전함
- 작업이 끝날 때까지 사용자의 중간 개입이 허용되지 않음

 

    2-2-2. 2세대 운영체제 (1960년대 전반기)

    - 트랜지스터 컴퓨터 + 자기코어 기억장치(주기억장치로 사용), 자기 디스크 팩(보조기억장치로 사용)

    - UNIVAC-Ⅱ → IBM 1400시리즈(크기 축소, 속도 증가)

    - 어셈블리언어, FORTRAN, COBOL 등 등장

    - 운영체제는 컴퓨터에 장착된 다양한 주변기기들을 효율적으로 관리하는 데 관심

    - Multiprogramming System & MultiProcessing System

    - Timesharing System & Interactive System

    - Realtime System

폰 노이만의 "내장 프로그램" 개념
- 어떠한 작업도 실행되기 위해서는 주기억장치에 있어야 한다.
다중 프로그램 시스템 (Multiprogramming System)
- 다수 개의 작업을 CPU에 넣어놓는 방식

다중 처리 시스템 (Multiprocessing System)
- 여러 개의 처리장치(CPU)를 장착하여 동시에 여러 작업을 병렬로 실행하는 방식
- CPU가 2개 이상

다중 처리 시스템은 여러 작업이 동시에 진행된다고 하였지만, 실행이 될 때 주기억장치에 있어야하기 때문에
다중 처리를 위해서는 다중 프로그래밍이 필요하다.
시분할 시스템 (Timesharing System)
CPU가 처리할 수 있는 시간을 작업 수에 맞춰 분할하여 각자에게 일정량만큼씩 분배하여 번갈아가며 처리하는 방식
시분할 시스템을 위해서는 다중 프로그래밍이 필요하다.

대화식 시스템 (Interactive System)
시분할 시스템으로 사용자에게 바로바로 응답할 수 있게 되었다.
→ 모니터, 키보드, 마우스 등을 이용해 시스템과 사용자가 대화하듯이 일을 처리해가는 방식

 

다중 프로그램 시스템으로 인해 시분할 시스템이 가능하게 되었고, 시분할 시스템을 위해 대화식 시스템이 필요하다.

 

2.2.3. 3세대 운영체제 (1960년대 후반기 - 1970년대)

- 집적회로(IC)의 개발 -> CPU에 적용

- 범용 컴퓨터 시스템 -> 컴퓨터 다양한 용도로 사용됨

- IBM 360계열: batch 시스템 기본적으로 탑재

- DEC: interactive 시스템 탑재

- 다중모드 시분할 시스템(Multi-inmode)

    - 일괄처리(batch), 시분할(time-sharing), 실시간 작업(real-time) 모두 지원

    - 기본적으로 시분할, 필요한 경우 batch, 실시간 등 지원함

- TCP/IP 표준과 함께 근거리 통신망(LAN) 등장

- UNIX 운영체제 출현

    - UNIX 이전에는 각 하드웨어마다 운영체제 존재, 소스코드 비공개

    - 소스코드 공개로 자연스럽게 널리 퍼져서 다양한 버전이 탄생함

 

2.2.4. 4세대 운영체제 (1970년대 후반기 - 현재)

- 고밀도 집적회로 

- 마이크로프로세서

    - 최소의 비용으로 최대의 효과

    - 분산 및 병렬 처리 시스템 등장

    - 데이터베이스, 인공지능, 운영체제의 표준안에 관한 연구도 활발히 진행

    - 모든 pc에 적용됨

 

3. OS 기능

- UNIX 구성 요소

    - 사용자 인터페이스(쉘)

        - 장치 관리

        - 파일 관리

        - 메모리 관리

        - 처리기 관리

 

- 쉘 (사용자 인터페이스)

    - GUI가 현재보다 발달하기 전:

        - command창에서 명령어 입력

        - command interpreter가 명령어 번역 후 명령어 실행파일 실행

        - 끝나면 다음 명령어 입력 대기

        - command interpreter == 사용자 인터페이스(쉘)

 

        - 모든 os에 command interpreter가 존재함. unix에서는 쉘이라는 이름으로 존재하는것

        - unix에서 /bin 디렉터리에 있는 실행파일들이 command 실행파일들임

 

        - 시스템에서 쓸 수 있는 모든 command들만으로 만들어진 프로그램: command procedure

        - 프로그램 설치할 때 기본 환경 확인, 설치, path 설정, 설정완료 등 모든 과정이 command로 이루어짐

        - 이 설치 프로그램: command precedure

        - 쉘이 command procedure까지 합쳐서 의미하는 경우도 있음

 

4. OS의 위치

- 운영체제는 크게 커널과 유틸리티 프로그램으로 나눔

 

- 커널

    - 운영체제의 기능들 중 빈번하게 사용되는 부분

    - 부팅될 때 주기억장치에 적재되어 종료될 때까지 상주

    - 유틸리티를 따로 둔 이유는 주기억장치의 용량 부족

    - 사용자 인터페이스의 대부분이 유틸리티에 속함

    - IO.SYS, MSDOS.SYS, COMMAND.SYS 등이 커널에 속함

    - 커널 중에서도 더 빠른 실행이나 높은 수준의 보호가 요구되는 프로그램들은 펌웨어 형태인 ROM이나 PLA로 만들어 놓음

 

- 듀얼모드

    - 유저모드 + 커널모드

    - 일반 사용자가 운영체제나 시스템 등을 임의로 수정하는 것을 막기 위해 그러한 일들은 운영체제가 담당하도록 커널 모드에서 실행

    - 사용자 프로그램이나 응용 프로그램은 유저모드에서 실행

    - 프로그램이 실행될 때 유저모드와 커널모드가 번갈아가면서 실행됨

    - ex)

total = 0

for i in range(1, 50):
    total += i
print(total)

    이건 소스코드이긴 하지만,

    컴파일이 될 때 print문은 모니터, 프린터 등 다른 HW를 건드려야하기 때문에 system call로 바뀐다.

    그리고 실행시키면 유저모드에서 실행되다가, system call을 만나면 커널모드로 바뀐다.

    I/O 담당 운영체제가 실행되어 print 대상 device를 확인하고, device가 실행중인지 확인 후 입출력을 실행한다.

    입출력이기 때문에 time sharing system 방식으로 cpu가 작동한다.

    end를 만나면 프로그램이 끝나며 다시 유저모드로 돌아간다.

    이처럼 간단한 프로그램에서도 유저모드와 커널모드가 번갈아가며 작동한다. 

'Software > OS' 카테고리의 다른 글

[OS] 4장. CPU 스케줄링  (0) 2022.04.18
[OS] 3장. 프로세스와 스레드  (0) 2022.04.11
[OS] 2장. OS 상식과 인터럽트  (0) 2022.04.10

출처) 코뮤니티 모각코 "JAVA를 자바" 과정

 

1. 예외처리가 필요할 때

오류에 부딪힐 때 오류를 없애는 것이 가장 좋지만, 모두 없앨 수 없다면 그것을 제대로 처리하는 것도 중요하다.

오류를 무시할 때도 있고, 적절한 조치를 취해야 할 때도 있다.

 

그 때 사용할 수 있는 것이 "try, catch, throw" 등이다.

 

2. try, catch, finally

 

1) try~catch

 

int[] value = new int[3];

value[3] = 10;

 

위 코드를 실행하면 ArrayIndexOutOfBoundsException 오류가 발생한다.

 

오류 발생 시 "오류 발생" 이라는 문장이 출력되도록 코드를 짜면 아래와 같다.

 

int[] value = new int[3];

 

try {

    value[3] = 10;

} catch (ArrayIndexOutOfBoundsException e) {

    System.out.println("오류 발생");

}

 

위 코드를 실행하면 오류 발생 이라는 문장이 출력된다.

이처럼 예외 처리는 try~catch 문으로 할 수 있다.

 

try {
	실행문
} catch(예외1) {
	예외1 발생 시 실행문
} catch(예외2) {
	예외2 발생 시 실행문
}

 

catch (ArrayIndexOutOfBoundsException e) {

    System.out.println("오류 발생");

}

 

이처럼 괄호 안에는 예외처리 할 오류의 형식과 오류를 칭할 이름(e)을 적는다.

 

2) finally

 

예외가 발생했을 때에도 무조건 실행하고 싶은 코드가 있을 경우 사용하는 것이 finally이다.

예를 들어, 나눗셈에서 0으로 숫자를 나누면 ArithmeticException 오류이다.

 

int num;try {    num = 4 / 0;} cath(ArithmeticException e) {    System.out.println("오류 발생");    num = -1;} finally {    System.out.println("무조건 실행");}

 

System.out.println(num);

 

위 코드는 ArithmeticException 오류가 발생했을 때 오류 발생 문장을 출력하고 num 변수에 -1을 저장한다.또한, 오류와 관계없이 무조건 실행이 출력된다.

 

실행결과는 위와 같다.

'Software > JAVA' 카테고리의 다른 글

[JAVA] Day2. 조건문, 반복문, 배열  (0) 2022.12.27
[JAVA] Day1. 자바 기초  (0) 2022.12.27
[JAVA] 객체지향  (0) 2021.07.22
[JAVA] 배열  (0) 2021.07.20
[JAVA] 스캐너로 입력받기  (0) 2021.07.15

출처) 코뮤니티 모각코 "JAVA를 자바" 과정

 

1. 객체지향 프로그래밍 (OOP) 구성요소

- 클래스, 객체, 메소드

 

1) 클래스

객체의 설계도 (틀) 

클래스를 이용해 비슷한 구성의 객체 찍어냄

 

    - 필드 : 클래스에 포함된 변수

    - 메소드 : 클래스 안에 있는 함수

    - 생성자 : 인스턴스가 처음 만들어질 때 실행될 초기화 메소드

 

* 클래스를 이용해 객체를 만드는 과정을 클래스의 인스턴스화라고 한다. 

   즉, A라는 클래스에서 만들어진 객체 = A클래스의 인스턴스

   객체 == 인스턴스

 

2) 메소드

클래스에 선언된 함수

 

public 리턴 자료형 메소드명 (입력1, 입력2 ...) {

    ...

    return 리턴값;

}

 

* public -> 메소드가 선언된 클래스 외부에서도 호출될 수 있는 메소드

 

3) 필드

클래스 안에서 선언된 변수

인스턴스로 변수에 데이터를 저장하고 저장된 데이터를 불러올 수 있음

 

2. 생성자

 

클래스가 인스턴스화 될 때 (객체가 만들어질 때) 반드시 호출되는 클래스의 구성요소

 

클래스와 동일한 이름으로 선언함

 

class Calculator {
    Calculator() {
        System.out.println("생성자 실행");
    }
}

public class Helloworld {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        System.out.println("calculator 생성 완료");
    }
}

 

이 예시는 Calculator 클래스에서 생성자가 호출될 때 "생성자 실행" 문구를 출력하도록 만들었다.

 

실행해보면 생성자 실행이 먼저 출력되고, calculator 생성 완료가 출력된다.

 

생성자는 보통 필드의 값을 초기화하기 위해 사용된다.

 

Calculator 클래스에 x, y라는 변수를 생성했다면 초기값을 설정해주어야 한다.

이렇게 초기값을 설정해 주는 것을 보통 생성자를 통해 한다.

 

class Calculator {
    int x;
    int y;
    Calculator(int a, int b) {
        this.x = a; // x에 a 대입
        this.y = b;
    }
}

 

이 예시의 경우 x, y 변수를 선언하고 생성자에서 int 자료형 데이터 2개를 받아 각각 x와 y에 저장한다.

메소드에서 입력 변수를 받는 방식과 비슷하다.

 

class Calculator {
    int x;
    int y;
    Calculator (int x, int y) {
        this.x = x;
        this.y = y;
    }
}

public class HelloWorld {
    public static void main(String[] args) {
        Calculator calculator = new Calculator(3, 5);
        System.out.println(calculator.x);
        System.out.println(calculator.y);
    }
}

 

Calculator 클래스에서 Calculator 생성자를 만들고, calculator 객체를 만들면서 생성자를 이용해 x = 3, y = 5로 초기화 한 것이다.

이를 실행하면 

3

5

이렇게 출력된다.

 

3. this 키워드

 

this는 인스턴스 (객체)를 가리키는 의미이다.

 

Calculato(int x, int y) {

    this.x = x;

    this.y = y;

 

위의 예제에서 this.x 는 인스턴스 내 필드를 가리키고, x는 입력받은 변수를 가리킨다.

'Software > JAVA' 카테고리의 다른 글

[JAVA] Day1. 자바 기초  (0) 2022.12.27
[JAVA] 예외처리  (0) 2021.07.22
[JAVA] 배열  (0) 2021.07.20
[JAVA] 스캐너로 입력받기  (0) 2021.07.15
[JAVA] 객체, 생성자, 계산기 예제  (0) 2021.01.11

수업 출처) 숙명여자대학교 소프트웨어학부 박동철 교수님, '데이터사이언스개론' 수업

 

1. how measure a model?

- 데이터 분석을 통해 "얻고싶은 것"이 무엇인지 신중히 고려하는 것이 중요하다.

 

- 모델의 수행 능력을 의미있는 방식으로 측정해야 한다.

    - 각 문제에 대한 올바른 평가 방식은 무엇일까?

    - ex) 이동통신사 고객 이탈 문제 (celluar-churn problem) : 정확한 예측의 비율? 이탈한 고객의 비율?

 

2. Evaluation Classifiers (예측모델 평가)

- 알지 못하는 클래스에 대해 이미 갖고 있는 데이터를 바탕으로 예측하는 모델이다.

- 이진 예측 모델의 클래스: positive / negative

- 이러한 모델이 얼마나 잘 수행되는지 어떻게 평가할 것인가?

 

2-1. Plain Accuracy

- 지금까지 우리는 classifier의 성능을 측정하기 위한 간단한 지표를 가정했다. : accuracy (or error rate)

 

- accuracy = 예측 중 맞은 개수 / 전체 개수 = 1 - error rate

 

- 측정하기 쉽고 이해하기 쉽기 때문에 많이 쓰이는 방법이다.

- 하지만, 너무 간단하고, 몇 개의 문제점들이 있다.

 

2-2. Confusion Matrix

- n개의 클래스를 분류하기 위한 n x n 행렬이다.

    - 행(rows) : 예측값

    - 열(columns) : 실제 값

    - 다른 종류의 오차(error)들을 개별적으로 보여준다. : False positives, False negatives

 

- p, n이 실제 클래스에 속하는 값들이고, y, n이 예측한 값들이다. 

- 따라서 y-p에 속하는 값들은 y로 예측했는데 실제로 positive인 값들이다.

 

2-3. Problems with Unbalanced classes

1) 분류 문제에서 한 클래스가 rare하다고 생각해보자.

- 예를 들어, 사기당한 고객을 찾는 경우나, 결함이 있는 부품을 찾는 경우나, 광고 등에 응하는 고객을 찾는 경우 그런 경우들이 흔치 않기 때문에 클래스 분포가 불균형하거나 비스듬할 것이다.

- 이러한 경우에는 accuracy로 성능을 판단하기 어렵다.

- 예를 들어 클래스 값이 999:1 비율이라면, 항상 999의 개체가 속한 클래스를 선택한다면 정확도는 99.9%가 되기 때문이다. 

- 비율 불균형이 그리 크지 않더라도 정확도는 오해의 소지가 매우 크다. 잘못된 해석이 도출될 수 있다.

 

- ex) cellular-churn problem

- 1000명의 churn 예측 모델을 A와 B가 각각 만들었다고 하자. 

- A모델은 churn 클래스는 100% 예측하고, not churn 클래스는 60% 예측한다.

- B모델은 churn은 60%, not churn은 100% 예측한다.

- 이 경우 두 모델은 매우 다르게 작동하지만 accuracy는 80%로 똑같다.

 

2) 또한, accuracy는 테스트 셋에 따라 달라진다. 

 

3) false positive와 false negative를 구별하지 않는다.

- 둘을 같이 세기 때문에 오차들이 모두 똑같이 중요한 것처럼 여겨진다.

- 하지만, 이는 현실 세계에서는 드문 경우이다.

- 다른 종류의 오차는 다른 무게를 갖는다.

- ex) 의학 진단 경우

    - false positive : 실제로 병에 걸리지 않았지만 걸렸다고 진단한 경우 (기분은 나쁠 수 있지만 실제로 정상이기 때문에 괜찮음)

    - false negative : 실제로 병에 걸렸지만 걸리지 않았다고 진단한 경우 (대처를 못하기 때문에 훨씬 심각한 문제를 초래함)

 

- ex) cellular-churn 

    - false positive : 떠날거라고 생각해서 상품을 제공했지만 떠나지 않음 (어쨌든 고객을 유지했기 때문에 괜찮음)

    - false negative : 떠나지 않을거라고 생각해서 제공하지 않았지만 떠남 (고객을 잃었기 때문에 훨씬 손해임)

 

- 대부분의 경우, 우리는 false positive와 false negative를 다르게 다뤄야 한다.

- classifier에 의한 각 결정들의 cost나 benefit을 추정해야 한다.

- 일단 집계되면, 이러한 것들은 classifier에 대한 기대이익(or 기대비용) 추정치를 산출할 것이다.

 

2-3. Generalizing beyond classification

- 데이터사이언스를 활용할 때 목표가 무엇인지, 주어진 목표에 맞게 데이터마이닝 결과를 적절히 평가하고 있는지 중요하게 생각해야 한다.

 

- ex) Regression

    - 데이터사이언스팀이 영화 추천 모델을 만든다고 생각해보자. 

    - 평점은 1 - 5개의 별을 줄 수 있고, 모델은 사용자가 보지 않은 영화를 얼마나 많이 시작할지 예측한다.

    - 모델을 오차의 평균으로 측정해야 할까, 오차 제곱의 평균으로 측정해야 할까?

    - 더 나은 방법이 있는지 확인해야 한다.

회귀모델을 위한 metrics

 

3. Expected Value (EV) : Key Framework

- EV는 데이터 분석적 사고를 돕는 아주 유용한 개념적 도구이다.

- 데이터 분석 문제에 대한 생각을 정리하는데에 아주 유용한 핵심 프레임워크를 제공한다.

- 다음은 EV의 일반적인 계산식이다.

- o𝑖 : 가능한 결정 결과 (ex: classification -> YES or NO)

- p(o𝑖) : o𝑖의 확률

- v(o𝑖): o𝑖의 가치 (ex: cost, benefit or profit)

 

3-1. Using EV for Classifier Use (ex. 타겟 마케팅)

- 각 소비자의 클래스를 예측하여 더 응답할 가능성이 높은 고객들을 예측하고자 한다.

- 불행히도 타겟 마케팅의 경우 각 소비자가 응답할 확률은 대부분 1-2%로 매우 낮다.

- 그래서 우리가 상식적으로 응답할만한 소비자를 결정하기 위해 임계값을 50%로 정한다면 아마 아무도 겨냥하지 못할 것이다.

- 아마 모든 소비자가 '응답하지 않을 것'이라고 분류될 것이기 때문이다.

- 하지만, EV 프레임워크를 활용하면 문제의 핵심을 찾을 수 있을 것이다.

 

- feature vector가 x인 고객들에게 pᵣ(x)를 부여하는 모델을 갖고 있다고 가정하자.

    - pᵣ(x)는 고객 x가 응답할 확률의 추정치이다.

    - 예를 들면 classification tree, logistic regression model 등이 있다.

 

- 그러면 이제 다음과 같이 타겟 고객 x의 기대 수익 (or 기대 비용)을 계산할 수 있다.

    - vᵣ : 우리가 응답으로부터 얻는 가치 (value from a response -> profit)

    - vₙᵣ : 무응답으로부터 얻는 가치 (value from no response -> cost)

 

- example scenario

    - 상품가격 $200

    - 원가 $100

    - 마케팅비 $1

 

- EV 계산식에서 vᵣ = $99, vₙᵣ = -$1 

- 우리는 EV가 0보다 크기만 해도 이익을 얻는 것이다.

따라서 계산을 해보면 pᵣ(x) · $99 - (1 - pᵣ(x)) · $1 > 0

=> pᵣ(x) > 0.01 

- 고객 x가 응답할 확률이 1%만 돼도 남는 장사라는 것을 알 수 있다.

- 따라서 우리가 예상했던 50% 응답률이 아니라, 1% 보다 큰 확률 추정치를 갖는 고객을 타겟으로 삼아야 한다.

 

3-2. Using EV for Classifier Evaluation

- 지금까지 개개인의 결정에 주목했다면, 지금부터는 '결정의 집합'에 더욱 주목하고자 한다.

- 특히 우리는 모델로부터 만들어진 일련의 결정들을 평가해야 한다.

 

- 이 평가를 할 때 서로 다른 모델들을 비교하는 것이 필요하다.

    - 우리의 데이터 중심 모델은 직접 만든 모델보다 더 성능이 좋은가?

    - 분류 트리 모델이 로지스틱 회귀 모델보다 더 성능이 좋은가?

 

- 총체적으로 우리가 관심을 가지는 것은 각 모델이 얼마나 잘 수행하는지, 즉 그것의 expected value가 무엇인지이다.

- EV의 일반 계산식이다.

- 각 o𝑖는 예측 클래스와 실제 클래스의 가능한 조합 중 하나에 해당한다.

((Y,p),(Y, n), (N, p). (N, n))

 

- 그리고 p(o𝑖)는 이미 confusion matrix에 나와있다.

- 각 o𝑖는 confusion matrix의 각 셀에 해당한다.

 

- ex) 

EV의 v(o𝑖)를 구하는 방법은 아래에서 다룬다.

 

1) step 1 : 각 케이스의 비율을 측정한다. (p(o𝑖))

- 각각의 count를 총 개체 수로 나눈다.

- p(h, a) = count(h, a) / T

    - h : 예측된 클래스 (Y / N)

    - a : 실제 클래스 (p / n)

    - T : 총 개체 수

    - count(h, a) : (h, a)케이스에 해당하는 개체 수

    - p(h, a) : (h, a)케이스의 비율 (or 확률 추정치)

 

2) step 2 : 각 케이스의 value를 명시한다. (v(o𝑖))

 

- 하지만, b(h, a)와 c(h, a)는 데이터로부터 추정할 수 없다.

- 이 둘은 일반적으로 분석으로부터 제공되는 외부 정보에 의해 정해진다.

- 그것들을 지정하는 것은 많은 시간과 노력이 필요하다. (ex: 우리가 고객을 유지하는 것이 정말 어느정도의 가치가 있는가?)

 

- ex) 위의 example scenario 예제

    - v(Y, n) = -1

    - v(N, p) = 0

    - p(Y, p) = 99

    - p(N, n) = 0

 

3) step 3 : total expected value (or profit) 을 계산한다.

- 다음 공식을 통해서 모델의 expected profit을 계산한다.

 

- final value는 각 고객 당 total expected profit을 나타낸다.

- 따라서 우리가 필요한 것은

    - 테스트 데이터셋에 대한 confusion matrix 계산

        - p(Y, p), p(Y, n), p(N, p), p(N, n)

    - cost-benefit matrix 생성

        - v(Y, p), v(Y, n), v(N, p), v(N, n)

 

3-3. Alternative Calculation of EV 

- alternative calculation은 종종 실제로! 사용되는 것이다.

- 왜냐하면 우리가 모델 비교 문제를 어떻게 다뤄야 하는지 정확히 알 수 있기 때문이다.

 

- 조건부확률

    - 사전 class : p(p) and p(n)

        - 각각 positive와 negative 개체일 확률이다.

    - p(x, y) = p(y) · p(x | y)

    - p(x | y) = p(x, y) / p(y)

        - (p(x, y) : x 와 y의 교집합)

 

- 다음과 같이 사전 클래스 p(p)와 p(n)을 찾아낼 수 있다.

 

- ex)

- expected profit = p(p) · [p(Y | p) · b(Y , p) + p(N | p) · b(Y , p)] + p(n) · [p(Y | n) · b(Y, n) + p(N | n) · b(N, n)]

    = 0.55 (0.92 · 99 + 0.08 · 0) + 0.45 (0.14 · -1 + 0.86 · 0) 

    ≈ $50.04

- 이를 통해 각 고객 당 평균적으로 약 $50 의 이익을 얻을 수 있다고 예측할 수 있다.

 

- 이제 경쟁 모델에 대한 정확도를 계산하는 대신에, expected values를 계산할 것이다.

- 나아가, 다양한 분포에 대해 두 모델을 쉽게 비교할 수 있다.

    - 각 분포에 대해 우리는 쉽게 사전확률을 대체할 수 있다.

    - 예를 들어, 불균형한 분포는 p(p) = 0.7, p(n) = 0.3 // 균등한 분포는 p(p) = 0.5, p(n) = 0.5라고 할 수 있다.

 

    - expected profit = p(p) · [p(Y | p) · b(Y , p) + p(N | p) · b(Y , p)] + p(n) · [p(Y | n) · b(Y, n) + p(N | n) · b(N, n)] 에서

       p와 n의 분포가 아무리 바뀌어도 [ ] 속에 있는 비율들은 변하지 않기 때문에 p(p)와 p(n)의 값만 바꾸어서 계산하면 된다.

    - p(p), p(n)을 제외한 다른 비율들이 변하지 않는 이유는 같은 모델이기 때문이다. 

 

4. Overview of the EV Calculation

 

5. Other Evaluation Metrics

- 데이터 사이언스에 사용되는 많은 평가 지표들이 있다.

- 모든 지표들은 근본적으로 confusion matrix의 요약본이다.

- Accuracy = (TP + TN) / (TP + FN + FP + TN) : 전체 개체 중 맞은 예측의 비율

- True positive rate (=Sensitivity) = TP / (TP + FN) = 1 - False negative rate : positive 중 맞춘 예측의 비율

- False negative rate = FN / (TP + FN) : positive 중 틀린 예측의 비율

- True negative rate (=Specificity) = TN / (FP + TN) = 1 - False positive rate : negative 중 맞춘 예측의 비율

- False positive rate = FP / (FP + TN) : negative 중 틀린 예측의 비율

 

- Precision 정밀도 = TP / (TP + FP) : positive 로 예측한 경우에서의 정확도

- Recall 재현율 = TP / (TP + FN) =True positivve rate (Sensitivity)

- F-measure F-값 = 2 x {(Precision x Recall) / (Precision + Recall)} : precision과 recall의 조화평균

- 이 세 가지는 정보 검색과 패턴 인식 (텍스트 분류) 에 널리 사용된다.

 

6. Baseline Performance

- 몇몇 경우, 우리는 모델이 어떤 기준선보다 더 나은 성능을 갖고있다고 증명해야 한다.

- 그렇다면 비교의 대상이 되는 적절한 기준선은 무엇일까?

    - 그것은 실제 용도에 따라 다르다.

    - 하지만 몇몇 일반적인 원칙들이 있다.

 

1) Random model

- 통제하기 쉬울 수 있지만, 그다지 흥미롭거나 유익하진 않을 것이다.

- 매우 어려운 문제나 처음 문제를 탐색할 때 유용할 것이다.

 

2) Simple (but not simplistic) model

- ex) weather forecasting

    - 내일의 날씨는 오늘과 같을 것이다.

    - 내일의 날씨는 과거부터 내일까지의 날씨의 평균일 것이다.

    - 각 모델은 랜덤으로 예측하는 것보다 훨씬 나은 성능을 발휘한다. 어떤 새롭고 더욱 복잡한 모델도 이것들보다는 나은 성능을 가져야 한다.

 

3) The majority classifier 다수결

- 항상 훈련 데이터셋에서 다수의 클래스를 선택하는 방법이다.

 

- 우리는 모집단의 평균 값이라는 직접적으로 유사한 기준선을 가지고 있다. 

- 보통 평균이나 중간값을 사용한다.

 

- ex) 추천 시스템    - 특정 영화가 주어졌을 때 고객이 몇개의 별점을 줄 지 예측해보자.    - random model     - simple models        - model 1 : 고객이 영화들에게 준 별점의 평균 값을 사용할 것이다.        - model 2 : 대중이 그 영화에 준 별점의 평균 값을 사용할 것이다.    - combination of multiple simple models : 이 둘에 기초한 간단한 예측은 둘 중 하나를 따로 사용하는 것보다 훨씬 낫다.

 

- 간단한 모델을 비교하는 것 외에도, 배경 지식을 기반으로 한 간단하고 저렴한 모델을 기본 모델로 사용할 수 있다.

 

- ex) Fraud detection 사기 탐지    - 일반적으로 대부분 사기당한 계정들은 갑자기 사용률이 늘어난다고 알려져 있다.    - 거래량 급상승에 대한 계좌 조회는 많은 사기를 잡기에 충분했다.    - 이 아이디어를 구현하는 것은 간단하며, 유용한 기준선을 제공했다.

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

 

1. 순서 리스트

- 원소들을 순서에 따라 배열한 리스트이다.

- (ex) days of week, months of year, ...

- C언어의 경우 같은 자료형으로 이루어져 있다.

 

- Array 배열

    - 선언한 뒤 원소의 개수를 바꿀 수 없다.

    -따라서 크기를 잘 설정하지 않으면 공간이 부족하거나 메모리 낭비가 발생할 수 있다.

    - 메모리에서 연속적으로 할당되며, 인덱스로 접근한다.

    - 중간에 원소를 추가하거나 제거하기도 어렵다.

 

- Linked list 연결 리스트

    - 메모리에 분산된 장소로 저장되어 있다.

    - 즉, 노드들이 메모리에 흩어져 있지만, 포인터로 연결되기 때문에 순서는 존재한다.

    - 크기 조절이 가능하다.

    - 중간에 원소 변경도 가능하다.

    - 하지만, 관리를 잘못했을 때 overhead 등 심각한 오류가 발생할 수 있다.

    - 사용자가 직접 연결 리스트의 노드를 실행 중에 관리하는 것을 '동적 메모리 관리'라고 한다.

 

2. 연결 리스트

- 특징

    - 원소들은 메모리 상에서 물리적으로 분산되어 있다.

    - 논리적으로는 포인터로 연속적으로 연결되어 있다.

    - 원소들은 필요할 때 생성될 수 있다.

 

- 표현

    - 연결 리스트의 원소는 '노드'라고 부르며 구조체로 선언된다.

    - 데이터 필드 : 정보를 저장한다. 데이터 필드를 추가함으로써 구조체를 확장할 수 있다.

    - 링크 포인터 필드 : 노드와 다음 노드를 연결하는 연결 포인터이다.

    - 연결 리스트의 노드는 자신과 동일한 구조체 자료형을 참조하기 때문에 자기 참조형 구조체라고도 부른다.

 

- ex

#include <stdio.h>

typedef struct node_type * note_ptr;

struct node_type
{
    int data;
    note_ptr link;
};

void main()
{
    struct node_type node1, node2, node3;
    node1.data = 7;
    node2.data = 11;
    node3.data = 13;
    node1.link = &node2;
    node2.link = &node3;
    node3.link = NULL;
    printf("%d %d %d", node1.data, node2.data, node3.data);
}

3개의 노드가 링크로 연결된 연결 리스트 예제이다. 

노드는 node_type 구조체형으로 선언되고, 링크 포인터는 구조체에 대한 포인터이며 node_ptr로 선언된다.

이 연결 리스트는 정적 메모리 공간에 노드 객체를 생성하여 연결한 경우이다.

따라서 실행 중에는 노드를 위한 공간을 할당할 수 없다.

 

- 연결 리스트 노드의 멤버에 접근할 때에는 ' -> ' 화살표 연산자를 사용한다.

- node1 -> data

- 필요 없는 노드는 free함수를 통해 해제하고 힙 공간으로 반환하는 것이 좋다.

 

따라서 위의 코드는 다음과 같이 나타낼 수 있다.

//node1.data = 7;
node1 -> data = 7;

//node2.data = 11;
node2 -> data = 11;

//node1.link = &node2;
node1 -> link = node2;

//node2.link = &node3;
node2 -> link = node3;

printf("node1 : %d", node1 -> data);

 

주소 변수 메모리
100 1000 node1 정적 메모리
     
110 1300 node2
     
120 1200 node3
     
1000 7 data 동적 메모리
  1200 link
1200 13 data
  NULL link
1300 11 data
  1200 link
     

- 자료형으로 선언된 변수인 node1, node2, node3 은 정적 공간에 할당된 반면, malloc 함수에 의해 생성된 연결 리스트 노드들은 힙 메모리에 저장되어 있다. 

 

3. 동적 메모리 할당

- 정적 메모리 : 컴파일 할 때 메모리가 할당된다. 자료형이 있고, 선언되는 모든 변수가 정적 메모리를 가진다.

- 동적 메모리 : 실행될 때 메모리가 할당된다.

 

3-1. Operators

- malloc 

    - 지정된 크기의 메모리 할당을 요청한다.

    - 할당된 장소의 주소를 반환한다.

    - malloc으로 할당된 메모리 공간은 free() 를 선언하기 전까지 사라지지 않는다.

 

- free 

    - 할당된 메모리 공간을 해제하는 함수이다.

    - 해제하지 않으면 메모리 고갈이 발생한다.

 

3-2. Example (dynamic memory == heap memory)

void heap_test()
{
    int *pi;
    float *pf;
    
    pi = (int *) malloc (sizeof(int));
    pf = (float *) malloc (sizeof(float));
    *pi = 1024;
    *pf = 3.14;
    
    printf("%d, %f, %u\n", *pi, *pf, &pi);
    free(pi);
    free(pf);
}

 

처음 *pi와 *pf는 정적 메모리 공간에 할당되었다.

 

    pi = (int *) malloc (sizeof(int));
    pf = (float *) malloc (sizeof(float));

 

이 두 문장으로 pi와 pf 에 각각 동적 메모리 공간을 가리키는 포인터를 할당하였다.

 

    *pi = 1024;
    *pf = 3.14;

 

이 두 문장은 각각의 포인터가 참조하는 공간에 해당하는 값을 저장하는 실행문이다. 

 

따라서 메모리 공간이 아래와 같이 할당되었을 때, 프린트문을 실행하면 결과값은 다음과 같이 나온다.

1024, 3.14, 100

 

 

4. 단방향 연결 리스트

- 노드들이 한 방향으로만 연결된 경우를 단방향 연결 리스트라고 한다.

- 첫번째 노드를 참조하는 포인터를 '리스트 포인터'라고 한다.

- 이를 통해 리스트 노드들을 탐색한다.

 

- 노드 삽입

    - malloc 함수로 삽입할 노드를 생성하고, 그 주소를 포인터 노드에 할당한다.

    - 생성된 노드의 데이터 필드에 값을 저장하고, 링크 필드에 NULL을 저장한다.

    - 연결 리스트 포인터가 list라고 가정할 때 

 

        1) 연결 리스트가 비어 있는 경우 (empty)

            - 삽입할 노드가 유일한 노드이므로 이 노드가 첫 노드가 되도록 리스트 포인터(list)에 노드의 주소 값을 저장하고 종료한다.

 

        2) 비어 있지 않은 경우

            - 리스트 중간에 삽입되는 경우 (앞 노드가 있는 경우)

            → 앞 노드의 링크 필드 값을 노드의 링크에 저장한다.

            → 앞 노드의 링크 필드에 노드의 주소 값을 저장하고 종료한다.

 

            - 리스트 맨 앞에 삽입되는 경우

            → 삽입할 노드의 링크 필드에 연결리스트 포인터 값 지정한다.

            → 연결리스트 포인터가 노드를 가리키도록하고 종료한다.

- malloc 함수로 새 공간을 요청해도 가용 힙 메모리가 더 이상 없다면 주소 값 대신 NULL 값이 반환된다.

 

- 선언

typedef struct list_node *list_ptr;
struct list_node {
    char data[4];
    list_ptr link;
};
list_ptr ptr = NULL;

 

- macro function for empty list

#define IS_EMPTY(ptr) (!ptr)

ptr이 NULL값이면 IS_EMPTY(ptr)은 1을 반환한다.

 

- 노드 초기화

ptr = (list_ptr) malloc (sizeof(struct list_node));
strcpy(ptr -> data, "com");
ptr -> link = NULL;

 

- 노드 삽입

#include <stdio.h>
#include <stdlib.h>

typedef struct node_type * node_ptr;
struct node_type
{
    int data;
    node_ptr link;
};

node_ptr list;
void insert_node(node_ptr prev, int data);
void print_list(node_ptr list);

void main()
{
    node_ptr node1, node2, node3;
    
    node1 = (node_ptr) malloc(sizeof(struct node_ytpe);
    node1 -> data = 100;
    node1 -> link = NULL:
    list = node1;
    
    node2 = (node_ptr) malloc(sizeof(struct node_ytpe);
    node2 -> data = 200;
    node2 -> link = NULL:
    list = node2;
    
    print_list(list);
    insert_node(node1, 150);
    printf_list(list);
}

void insert_node(node_ptr prev, int data)
{
    node_ptr node;
    node = (node_ptr) malloc (sizeof(struct node_type));
    if (!node) exit(1);
    node -> data = data;
    node -> link = NULL;
    
    if (!list) {
        list = node;
    }
    else if(prev) {
        node -> link = prev -> link;
        prev -> link = node;
    }
    else {
        node -> link = list;
        list = node;
    }
}

void print_list (node_ptr list) 
{
    while (list) {
        printf("%d ", list -> data);
        list = list -> link;
    }
    printf("\n");
         
}               

 

- 노드 삭제

 

    1) 리스트가 비어 있는 경우 

        - 그대로 종료한다.

    2) 리스트에 노드가 존재하는 경우 

    2-1) 중간 노드 삭제

        - 삭제 노드의 앞 노드의 링크 필드에 삭제할 노드의 링크를 저장한다.

    2-2) 맨 앞 노드 삭제

        - 리스트 포인터에 삭제할 노드의 링크를 저장한다.

    - 노드를 삭제하고 종료한다.

 

#include <stdio.h>
#include <stdlib.h>

typedef struct node_type * node_ptr;
struct node_type
{
    int data;
    node_ptr link;
};

node_ptr list;
void delete_node (node_ptr prev, node_ptr node);
void print_list (node_ptr list);

void main()
{
    node_ptr node1, node2, node3;
    node1 = (node_ptr) malloc (sizeof(struct node_type));
    node1 -> data = 100;
    node1 -> link = NULL;
    list = node1;
    
    node2 = (node_ptr) malloc (sizeof(struct node_type));
    node2 -> data = 200;
    node2 -> link = NULL;
    node1 -> link = node2;
    
    node3 = (node_ptr) malloc (sizeof(struct node_type));
    node3 -> data = 300;
    node3 -> link = NULL;
    node2 -> link = node3;
    
    print_list(list);
    delete_node(node1, node2); // node1 : prev, node2 : delete
    print_list(list);
}

void delete_node(node_ptr prev, node_ptr node)
{
    if (prev) {
        prev -> link = node -> link;
    }
    else {
        list = node -> link;
    }
    free(node);
}

void print_list(node_ptr list)
{
    while(list) {
        printf("%d", list -> data);
        list = lsit -> link;
    }
    printf("\n");
}

 

5. 연결 리스트 연산자

5-1. 연결 리스트 길이

- 노드 수로 계산한다.

#include <stdio.h>
#include <stdlib.h>

typedef struct node_type * node_ptr;
struct node_type
{
    int data;
    node_ptr link;
};

int length_list(node_ptr list);

void main()
{
    node_ptr list, node1, node2, node3;
    node1 = (node_ptr) malloc (sizeof(struct node_type));
    node1 -> data = 100;
    node1 -> link = NULL;
    list = node1;
    
    node2 = (node_ptr) malloc (sizeof(struct node_type));
    node2 -> data = 200;
    node2 -> link = NULL;
    node1 -> link = node2;
    
    node3 = (node_ptr) malloc (sizeof(struct node_type));
    node3 -> data = 300;
    node3 -> link = NULL;
    node2 -> link = node3;
    
    printf("list lentgh is %d\n", length_list(list));
}

int lentgh_list (node_ptr list)
{
    int count = 0;
    node_ptr temp = list;
    while (temp)
    {
        count ++;
        temp = temp -> link;
    }
    return count;
}

 

5-2. 연결 리스트의 결합

- 두 개의 연결 리스트를 연결하여 하나로 합치는 것이다. 

- 결합 연산은 앞 리스트의 노드 수에 비례하여 시간이 걸리므로, 시간복잡도는 O(첫번째 리스트의 길이) 가 된다.

 

- 첫 번째 리스트가 빈 리스트라면 두 번째 리스트를 그대로 반환하고 종료한다.

- 첫 번째 리스트가 빈 리스트가 아니라면 두 번째 리스트를 확인한다.

    - 두 번째 리스트가 빈 리스트라면 첫 번째 리스트를 그대로 반환하고 종료한다.

    - 두 번째 리스트가 빈 리스트가 아니라면 결합하기 위해 첫 번째 리스트의 마지막 노드를 탐색하고, 마지막 노드의 링크 필드에 두 번째 리스트 포인터(list2)를 저장한 후 첫 번째 리스트 포인터(list1)를 반환하고 종료한다.

 

#include <stdio.h>
#include <stdlib.h>

typedef struct node_type * node_ptr;
struct node_type
{
    int data;
    node_ptr link;
};

node_ptr concat_list(node_ptr list1, note_ptr list2);
void print_list(list1);

void main()
{
    node_ptr list1, list2, node1, node2;
    list1 = (node_ptr) malloc (sizeof(struct node_type));
    list1 -> data = 100;
    
    node1 = (node_ptr) malloc (sizeof(struct node_type);
    node1 -> data = 150;
    node1 -> link = NULL;
    
    list1 -> link = node1;
    
    list2 = (node_ptr) malloc (sizeof(struct node_type));
    list2 -> data = 200;
    
    node2 = (node_ptr) malloc (sizeof(struct node_type));
    node2 -> data = 250;
    node2 -> link = NULL;
    
    list2 -> link = node2;
    
    print_list(list1);
    print_list(list2);
    list1 = concat_list(list1, list2);
    print_list(list1);
}

node_ptr concat_list (node_ptr list1, node_ptr list2)
{
    node_ptr temp;
    if (!list1)
        return list2;
    else {
        if (list2) {
            temp = list1;
            while (temp -> link)
                temp = temp -> link;
                
            temp -> link = list2;
        }
        return list1;
    }
}

void print_list(node_ptr list)
{
    while (list) {
        printf("%d", list -> data);
        list = list -> link;
    }
}

 

6. 순환 연결 리스트

- 연결 리스트의 마지막 노드의 링크가 NULL이 아니고, 그 리스트의 첫 노드에 다시 연결되어 있는 형태를 말한다.

- 시작 노드가 변경되어도 전체 노드들이 여전히 연결되어 있는 상태이기 때문에 시작 노드 변경이 용이하다.

 

- 순환 연결 리스트의 길이

    - 시작노드로 다시 되돌아올 때가 전체 노드를 모두 탐색한 시점이다.

    - do - while 문 사용

 

#include <stdio.h>
#include <stdlib.h>

typedef struct node_type * node_ptr;
struct node_type
{
    int data;
    node_ptr link;
};

int clist_length(node_ptr list);

void main() 
{
    node_ptr list, node1, node2, node3;
    node1 = (node_ptr) malloc (sizeof(struct node_type);
    node1 -> data = 7;
    list = node1;
    node2 = (node_ptr) malloc (sizeof(struct node_type));
    node2 -> data = 11;
    node1 -> link = node2;
    node3 = (node_ptr) malloc (sizeof(struct node_type));
    node3 -> data = 13;
    node2 -> link = node3;
    node3 -> link = node1;
    
    printf("circular list length = %d", clist_length(list));
}

int clist_length(node_ptr list)
{
    node_ptr move;
    int num = 0;
    if (list) {
        move = list;
        do {
            num ++;
            move = move -> link;
        } while (move != list);
    }
    
    return num;
}

 

7. 역순 연결 리스트

- 리스트 노드의 연결 순서를 반대로 뒤집어서 리스트로 반환하는 형식이다.

- (ex) [7 - 11 - 13] → [13 - 11 - 7]

- 시간 복잡도는 리스트의 길이(노드의 개수)에 비례한다.

 

- 리스트가 비어있거나 노드가 1개만 있는 경우 그대로 리스트 포인터(one)를 반환하고 종료한다.

- 리스트에 노드가 2개 이상인 경우 2개의 임시 포인터 변수(two, three)를 더 사용하여 연결 순서를 변환한다.

    - 노드 포인터(three)가 노드 포인터(two)를 참조하게 한다.

    - 노드 포인터(two)가 노드 포인터(one)를 참조하게 한다.

    - 노드 포인터(one)를 다음 노드로 이동시킨다.

    - 노드 포인터(two)의 링크 필드에 노드 포인터(three) 값을 저장하여 두 노드를 연결 시킨다.

    - 노드 포인터(one)가 NULL이 아닐 때까지 반복한다.

 

#include <stdio.h>
#include <stdlib.h>

typedef struct node_type * node_ptr;
struct node_type 
{
    int data;
    node_ptr link;
};

node_ptr reverse_list(node_ptr);
void print_list(node_ptr);

void main()
{
    node_ptr list, node1, node2, node3;
    node1 = (node_ptr) malloc (sizeof(struct node_type));
    node1 -> data = 7;
    list = node1;
    
    node2 = (node_ptr) malloc (sizeof(struct node_type));
    node2 -> data = 11;
    node1 -> link = node2;
    
    node3 = (node_ptr) malloc (sizeof(struct node_type));
    node3 -> data = 13;
    node2 -> link = node3;
    node3 -> link = NULL;
    
    print_list(list);
    list = reverse_list(list);
    print_list(list);
}

node_ptr reverse_list(node_ptr one)
{
    node_ptr two, three;
    two = NULL;
    while (one) {
         three = two;
         two = one;
         one = one -> link;
         two -> link = three;
    }
    return two;
}

void print_list(node_ptr list)
{
    while(list) {
        printf(%d ", list -> data);
        list = list -> link;
    }
}

 

8. 양방향 연결 리스트          

- 각 노드가 자신의 이전과 이후 노드에 대한 링크 포인터를 가지고 있다.

- 노드 삽입 또는 삭제 시 앞 노드를 추가로 알려줄 필요가 없기 때문에 편리하다.

- 대표적으로 이진 트리에 사용된다.

 

- node == node -> llink -> rlink == node -> rlink -> llink

 

- 단방향 연결 리스트와 달리 모든 노드의 링크 필드 값을 채워야 한다.

- 따라서 삭제할 수 없는 head 노드가 사용된다.

- 시작 노드의 llink 필드는 head 노드를 참조하도록 초기화된다.                  

// 양방향 연결 리스트의 선언

typedef struct node_type * node_ptr;
struct node_type 
{
    node_ptr llink;
    int data;
    node_ptr rlink;
};

 

- 노드 삽입

    - 노드의 llink를 prev 노드로 연결한다.

    - 노드의 rlink를 prev 다음 노드로 연결한다.

    - prev 다음 노드의 llink가 노드를 참조하도록 연결한다.

    - prev 노드의 rlink가 노드를 참조하도록 연결한다.

 

#include <stdio.h>
#include <stdlib.h>

typedef struct dll_node_type * dll_node_ptr;
struct dll_node_type 
{
    dll_node_ptr llink;
    int data;
    dll_node_ptr rlink;
};

void insert_dll (dll_node_ptr prev, dll_node_ptr node);
void print_dll_list (dll_node_ptr list1);

void main()
{
    dll_node_ptr head, list, node1, node2, node3;
    head = (dll_node_ptr) malloc (sizeof(struct dll_node_type));
    node1 = (dll_node_ptr) malloc (sizeof(struct dll_node_type));
    node1 -> data = 7;
    list = node1;
    
    head -> llink = NULL;
    head -> rlink = node1;
    node1 -> llink = head;
    
    node2 = (dll_node_ptr) malloc (sizeof(struct dll_node_type));
    node2 -> data = 13;
    node2 -> llink = node1;
    node2 -> rlink = head;
    node1 -> rlink = node2;
    
    node3 = (dll_node_ptr) malloc (sizeof(struct dll_node_type));
    node3 -> data = 11;
    node3 -> llink = NULL;
    node3 -> rlink = NULL;
    head -> llink = node2;
    
    print_dll_list(head, list);
    insert_dll(node1, node3);
    print_dll_list(head, list);
}

void insert_dll (dll_node_ptr prev, dll_node_ptr node)
{
    node -> llink = prev;
    node -> rlink = prev -> rlink;
    prev -> rlink -> llink = node;
    prev -> rlink = nodel
}

void print_dll_list (dll_node_ptr head, dll_node_ptr list) 
{
    while (list != head) {
        printf("%d ", list -> data);
        list = list -> rlink;
    }
    printf("\n");
}

 

- 노드 삭제

    - head 노드는 삭제가 불가능하다.

    - 일반 노드의 삭제일 경우 삭제할 노드의 이전 노드의 rlink에 삭제할 노드의 다음 노드 주소를 저장한다.

    - 삭제할 노드의 다음 노드의 llink에 노드 이전 노드를 복사한다.

 

#include <stdio.h>
#include <stdlib.h>

typedef struct dll_node_type * dll_node_ptr;
struct dll_node_type 
{
    dll_node_ptr llink;
    int data;
    dll_node_ptr rlink;
};

void delete_dll (dll_node_ptr head, dll_node_ptr node);
void print_dll_list (dll_node_ptr head, dll_node_ptr list);

void main()
{
    dll_node_ptr head, list, node1, node2, node3;
    head = (dll_node_ptr) malloc (sizeof(struct dll_node_type));
    node1 = (dll_node_ptr) malloc (sizeof(struct dll_node_type));
    node1 -> data = 7;
    list = node1;
    
    head -> llink = NULL;
    head -> rlink = node1;
    node1 -> llink = head;
    
    node2 = (dll_node_ptr) malloc (sizeof(struct dll_node_type));
    node2 -> data = 13;
    node2 -> llink = node1;
    node1 -> rlink = node2;
    
    node3 = (dll_node_ptr) malloc (sizeof(struct dll_node_type));
    node3 -> data = 11;
    node3 -> llink = node2;
    node3 -> rlink = head;
    node2 -> rlink = node3;
    head -> llink = node3;
    
    print_dll_list(head, list);
    insert_dll(node1, node3);
    print_dll_list(head, list);
}

void insert_dll (dll_node_ptr prev, dll_node_ptr node)
{
    node -> llink = prev;
    node -> rlink = prev -> rlink;
    prev -> rlink -> llink = node;
    prev -> rlink = nodel
}

void print_dll_list (dll_node_ptr head, dll_node_ptr list) 
{
    while (list != head) {
        printf("%d ", list -> data);
        list = list -> rlink;
    }
    printf("\n");
}

                                                                                                                                                                     

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

[Python] 1. Introduction  (0) 2022.03.08
[자료구조] 탐색  (0) 2021.04.20
[자료구조] 선형 자료구조  (0) 2021.04.18
[자료구조] 알고리즘 성능 분석  (0) 2021.04.17
[자료구조] Recursion 재귀 호출  (0) 2021.04.17

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

 

1. Structure 구조체

- 자료형이 상이한 여러 멤버들을 하나의 자료로 묶어서 처리할 수 있는 복합 자료형

 

1-1. 구조체 변수 선언

// ex1

struct student
{
    int age;
    int grade;
    char major[20];
} kim, lee;

// ex2

struct
{ 
    int age;
    int grade;
    char major[20];
} kim, lee;

// ex3

struct student
{
    int age;
    int grade;
    char major[20];
};
struct student kim, lee;

 

세 가지 모두 kim과 lee라는 구조체 변수를 선언하는 문장이다.

첫 번째 방식과 같이 구조체의 이름을 줄 수 있고, 두 번째 방식과 같이 구조체 변수를 생성할 수 있다. 세 번째 방식은 구조체 변수를 struct 키워드를 이용해서 선언하는 방식이다. 

 

- 구조체 변수를 통해서 멤버에 값 저장

student.grade = 3;
strcpy(kim.major, "Software Convergence");

이와 같이 "." 연산자를 사용한다.

 

1-2. 구조체 자료형 선언

- 일반적으로 구조체를 사용할 때에는 typedef와 함께 사용되는데, typedef 키워드는 struct student_type{...}와 같이 긴 자료형 이름을 student 로 대체하여 구조체 변수를 선언할 수 있게 해준다.

 

// ex1

typedef struct student_type
{
    char name[10];
    int grade;
    char major[20];
} student;

// ex2

typedef struct
{ 
    char name[10];
    int grade;
    char major[20];
} student;

student kim, lee;

 

이처럼 struct만 쓴다면 } 이후 나오는 것은 그 구조체 자료형을 가진 "변수" 인 반면, typedef struct로 선언을 했을 때 } 뒤에 나오는 것은 구조체 "자료형" 이다.

 

1-3. 구조체 할당과 비교

 

- 할당

    - 동일한 자료형의 구조체 변수 간에 직접적인 할당 명령문은 가능하다.

    - (ex)

    studnet kim, lee;

    kim = lee; 

 

- 비교

    - 구조체 변수 간 직접 비교는 허용되지 않는다.

    - 구조체 멤버 별로 비교해주어야 한다.

    - (ex) 

    if (kim == lee) ...  // 오류 !!

 

    if (strcmp(kim.name, lee.name) ...

    if (kim.age != lee.name) ...

    if (kim.grade == lee.grade) ...

    이런식으로 멤버별로 비교해주면 된다.

 

    - 문자열 비교를 위해 사용된 strcmp 함수는 두 인자 값이 "다를" 경우 1을 반환한다.

 

1-4. 내장형 구조체

- 다른 구조체 변수를 멤버로 갖는 구조체

 

#include <stdio.h>
#include <string.h>

typedef struct Contact_type
{
    char phone[20];
    char email[20];
    char address[20];
} Contact;

typedef struct Student_type
{ 
    char name[20];
    int age;
    char major[20];
    int grade;
    Contact contact;
} Student;

void main() 
{
    Student kim;
    
    strcpy(kim.name, "ChulSoo Kim");
    kim.age = 22;
    strcpy(kim.major, "Software Convergence");
    kim.grade = 3;
    strcpy(kim.contact.phone, "010-3606-0418");
    strcpy(kim.contact.email, "kim@naver.com");
    strcpy(kim.contact.address, "Seoul, Korea");
}

 

student 구조체 안에 contact 구조체를 넣은 형태이다. 

 

2. Pointer 포인터

- 메모리 주소를 값으로 갖는 변수

- 간접 참조를 통해 원본 데이터를 공유하거나 변경할 수 있다.

- call by reference, 연결리스트 동적 메모리 관리 등에 사용된다.

 

2-1. "&" 과 "*"

- &

    - 모든 변수에 붙일 수 있는 "주소" 연산자이다. 

    - 해당 변수의 메모리 주소 값을 반환한다.

 

- *

    - 사용되는 위치에 따라 의미가 달라진다.

    - 반드시 포인터형 변수에만 적용해야 한다.

 

    - int i =3, j;

    - int *p;

 

    - p = &i;

        - i의 주소를 p에 할당한다.

 

    - kim = *p;

        - kim 변수에 p가 가리키는 주소에 저장된 "값"을 대입한다.

 

    - *p = j;

        - p가 참조하는 "공간"에 j의 값을 저장한다.

 

3. Array 배열

- 자료형이 동일한 원소들의 유한집합

- <index, value> → item

- 선언 이후 배열의 크기를 변경할 수 없다.

- 배열의 원소 수가 n개 라면 유효한 배열 인덱스의 범위는 [0, n-1] 이다. 

 

- 배열의 첫 번째 원소인 A[0] 을 "시작 주소 (α)"라고 부른다.   

   이후 원소들은 연속적인 메모리 주소에 저장된다. (α + sizeof(data type))

    - (ex) int → 4bytes 

    α = 100100 → 100104 → 100108 → ...

 

- 배열 이름 A는 시작 주소를 값을 갖고 있는 "포인터 상수"이다. 선언에 사용된 이름은 이후 다른 값(주소)을 가질 수 없다.

 

- A == &A[0]    : A[0]의 주소

- *A == A[0]   : A[0]의 값

 

3-1. 함수에 배열 전달

- 시작 주소를 가지고 있는 "배열 이름"만 전달하면 된다.

- 배열 포인터 관점에서는 call by value이지만, 배열의 관점에서는 call by reference 라고 볼 수 있다.

- int list[] == int *list

 

- 배열 이름 + 정수 → 첫번째 원소에서 정수의 원소 개수만큼 떨어져 있는 원소의 주소

- (ex) oneArray = 100 → oneArray + 1 = 104, oneArray +2 = 108

 

4. Polynomial 다항식

- 항들의 집합

 

- 다항식 추상자료형 연산자

    - Zero()

    - IsZero()

    - Coef (poly, expon)     : 계수 찾기

    - Lead_Exp (poly)     : 최고차항 지수

    - Attach (poly, coef, expon)     : 항 추가

    - Remove (poly, expon)

    - SingleMult (poly, coef, expon)     : 다항식과 단항의 곱셈

    - Add (poly1, poly2)

    - Mult (poly1, poly2)

 

 

- (ex) 다항식 배열 선언

#define MAX_TERMS 50

struct polynomial
{
    float coef;
    int expon;
};

struct polynomial terms[MAX_TERMS];

int avail = 0;

 

수업 출처) 숙명여자대학교 소프트웨어학부 수업 "데이터사이언스개론", 박동철 교수님

 

1. Data Science Process

 

데이터 사이언스의 기본 원리는 데이터 마이닝이다. 데이터 마이닝은 상당히 잘 이해된 단계를 가진 process이다.

 

데이터 사이언티스트는 실생활의 문제를 하위 작업으로 분리한다.

하위 작업들의 솔루션들이 모여서 전체적인 문제를 해결할 수 있다.

 

문제의 기저가 되는 공통적인 데이터 마이닝 작업이 존재한다. 

(ex) classification, regression, clustering, association rule discovery

 

좋은 데이터 과학자가 되기 위해서는 다양한 공통적인 데이터 마이닝 작업을 해결하는 방법을 알아야 하고, 문제를 이렇게 공통적인 작업들로 나눌 수 있어야 한다.

 

각각의 하위 작업들은 그에 맞는 데이터 마이닝 기법으로 병렬처리 된다. 작업을 나눌 때 마이닝 방법을 생각해놓는 것이 좋다.

 

2. Common Data Mining Tasks

 

- classification : 분류 

- regression (a.k.a. value estimation) : 회귀 (value 추정)

- similarity matching : 유사도 매칭

- clustering : 군집화

- co-occurrence grouping (a.k.a. association rule discovery) : 동시발생 (관계 규칙 발견)

- profiling (a.k.a. behavior description) : 프로파일링 (행동 묘사)

- link prediction : 연관성 예측 (ex. recommendation)

- data reduction : 데이터 사이즈 줄이기 (불필요한 데이터 제거, 형태 변환 등)

- causal modeling : 인과관계 모델링

 

2-1. Classification 분류

 

모집단의 각 개체가 어떠한 클래스의 집합에 속할지 예측하는 방법이다. 

 

 

주로 상호배타적인 클래스로 분류할 때 사용한다.

분류에서 끝나는 것이 아니고, 분류를 학습하여 "예측" 하는 데 활용한다. → 지도학습에 활용

 

각각의 개체를 instance라고 하고, 속성을 attribute라고 한다.

예측의 대상이 되는 속성(클래스)를 classificaiton target 이라고 한다.

그리고 그 타겟들은 이미 상호배타적으로 설정되어 있다.

 

예를 들면, 연봉, 성별, 나이 등의 데이터로 이 사람이 어떠한 물건을 살지 안살지 예측하는 것이다.

이때 타겟은 "물건을 산다" 라는 속성이고, 클래스는 Yes 또는 No 이다.

 

일반적인 과정 : 훈련 데이터셋 → 데이터의 클래스를 묘사하는 모델 설정 (모델링) → 새로운 인스턴스가 주어질 때, 해당 모델을 적용하여 추측된 클래스 생성

 

비슷한 작업으로 scoring이나 class probability estimation 이 있다.

이는 각 개체가 각 클래스에 속할 확률을 도출하는 것이다.

예를 들어, 위의 예시에서 (Lee, 42000, male, 44) → (YES : 80%, NO : 20%) 이렇게 각 개체에 대해 클래스를 가질 확률을 계산한다.

 

2-2. Regression 회귀

 

각 개체가 특정 변수에 대해 어떠한 숫자 값을 가질 것을 예측하는 모델이다.

value estimation 이라고도 불린다.

 

 

예를 들어, 특정한 키의 사람은 어떤 값의 몸무게를 가질지 예측하는 것이다.

 

일반적인 과정 : 훈련 데이터셋이 주어질 때, 각 개인에 대해 특정 변수 값을 묘사하는 모델 설정 → 새로운 개체에 모델을 적용하여 측정된 값 생성

 

 

classification과의 차이점

- classification은 인스턴스의 "클래스"를 예측하는 것이다.  (ex. YES / NO)

- regression은 인스턴스와 관련된 "숫자 값"을 예측하는 것이다.   (ex. 178)

 

2-3. Similarity Matching 유사도 매칭

 

알고있는 데이터를 기반으로 유사한 데이터를 찾는 모델이다.

 

 

일반적인 과정 : 두 개체 사이의 거리 측정 → 한 개체에 대해서 가장 작은 거리를 갖는 개체 탐색

 

데이터의 종류가 다양하기 때문에 거리 측정에서는 유클리드 거리, 코사인 거리 등 다양한 종류의 거리를 사용한다.

 

(유클리드 거리) 예를 들어서, User 3 = (5, 4, 7, 4, 7), User 4 = (7, 1, 7, 3, 8) → distance ≅ 3.87

 

2-4. Clustering 군집화

 

비슷한 개체를 하나의 군집으로 묶는 모델이다. 

유사도를 측정하기 위해 "거리"를 사용한다. 

 

산점도에서 자연스럽게 형성되는 그룹을 분석할 때 유용하다. → 비지도학습에 활용

 

 

예를 들면, 주로 어떤 종류의 고객을 보유하고 있는지 파악할 때 사용된다.

 

2-5. Co-occurrence grouping 동시발생

 

개체들 사이의 동시 발생을 통해 관계를 찾는 모델이다. 

 

예를 들어서, 마트 판매 상품을 분석해보니 기저귀를 살 때 맥주도 같이 사는 손님의 비율이 높았다.

 

이와 같이 분석을 통해 특별 프로모션, 상품 진열, 세트 판매, 추천 등 마케팅에 활용할 수 있다.

 

2-6. Profiling 행동 특성 묘사

 

개인이나 집단의 전형적인 행동을 특징짓는 모델이다. 

 

normal한 행동특성에서 벗어난 행동을 탐지할 때 매우 유용하다.

프로필은 normal한 행동을 묘사하기 때문에, 갑자기 그것에 벗어난 행동을 할 때 알림을 주는 것이다.

 

한 예시로는 사기 탐지에 사용된다.

 

2-7. Link Prediction 연관성 예측

 

데이터 아이템들 사이의 연관성을 예측하는 모델이다. 

일반적으로 연결고리가 존재한다고 제안하고, 그 강도를 추정함으로써 사용된다.

 

"추천" 알고리즘에 매우 유용하다. SNS에서 친구를 추천해주거나, 넷플릭스 등에서 영화를 추천할 때 주로 사용된다.

 

2-8. Data Reduction 데이터 사이즈 감소

 

큰 데이터셋을 중요한 정보를 많이 포함하는 작은 데이터셋으로 사이즈를 줄이는 것이다.

 

작은 데이터셋은 다루기 쉽고, 정보나 향상된 인사이트를 보다 더 잘 드러낸다. 

하지만, 정보의 손실 또한 일어나기 쉽다는 단점이 있다.

 

2-9. Causal Modeling 인과관계 모델링

 

다른 사건에 실질적으로 영향을 주는 사건을 찾는 모델이다. 

 

예를 들어 담배피는 사람들 중에 이가 누런 사람과 폐암에 걸린 사람들이 있다고 할 때,

이 누래짐과 폐암이 담배때문에 발생한건지, 아니면 그냥 그런 사람들이 담배를 피는건지 인과관계를 파악하는 것이다.

 

3. Supervised 지도학습 vs. Unsupervised 비지도학습 Methods

 

3-1. Supervised

- 지도학습은 training data를 통해 이미 정답을 알고 학습을 시키는 것이다.

- 명시되어 있는 특정한 타겟이 존재한다.

- (ex) classification

 

Supervised data mining

- 알고싶은 특정한 타겟이 존재한다 → 답이 존재한다.

- training dataset이 반드시 존재한다 → 각각의 target value가 존재하는

- target value는 label 이라고도 부른다. 

- (ex) classification, regression, causal modeling

 

3-2. Unsupervised

- 비지도학습은 정답을 모르고 학습을 시키는 것이다.

- 정의해야 할 명시되어 있는 특정한 타겟이 없다.

- (ex) clustering

 

Unsupervised data mining

- 특정한 타겟이 아닌, 어떠한 패턴을 찾는 것이 목적이다.

- training dataset이 필요하지 않다.

- (ex) clustering, co-occurrence grouping, profiling

 

4. Classification vs. Regression

 

둘 다 supervised data mining task이다.

 

4-1. Classification

- 타겟이 카테고리 값이다. (ex. YES / NO, HIGH / MID / LOW)

- (ex) 이 고객이 인센티브를 받으면 이 상품을 구매할까? → YES / NO

고객이 인센티브를 받으면 (S1, S2, S3) 중에서 어떤걸 구매할 가능성이 높을까? → S1 / S2 / S3

 

4-2. Regression

- 타겟이 숫자 값이다. (ex. 2.5, 68)

- (ex) 이 고객이 이 서비스에 얼마를 쓸까? → $2,500

 

5. Data Mining and its Results

데이터 마이닝에는 두가지 단계가 있다.

 

5-1. Mining phase

: Historical Data (Training Data) → Data mining → Model

 

- training data를 통해 패턴 찾기 or 모델링

- training data는 모든 값이 명시되어 있어야 한다.

 

5-2. Use phase

: New data item → Model → Result

 

- 새로운 데이터에 패턴이나 모델 적용 → 예측

- 새로운 데이터는 알려지지 않은 class value가 존재한다.

 

6. Data Mining Process

: CRISP - DM (Cross Industry Standard Process for Data Mining)

산업을 통틀어서 데이터 마이닝을 위한 표준화된 Process이다.

 

: Business Understanding → Data Understanding → Data Preparation → Modeling → Evaluation → Deployment

 

기본적인 틀은 저 순서이고, 계속적인 평가를 통해 이전 과정으로 돌아가기도 하고 수정을 거듭하면서 점차 성능이 향상된다.

 

6-1. Business Understanding

- 해결해야 할 비즈니스 문제를 이해한다.

    -대부분 모호하거나 폭넓거나 이해하기 어려운 문제이다.

 

- 해결해야 할 비즈니스 문제를 하나 이상의 데이터 사이언스 문제로 간주한다.

    - 데이터 과학자들에 의해 공식화된 창의적인 문제가 성공의 열쇠가 된다.

 

- 문제를 여러 개의 하위 작업으로 나누고, 각각의 데이터 사이언스 문제를 해결할 방법을 디자인한다.

    - classification, regression, clustering 등

    - 각 문제에 맞는 효과적인 툴을 사용할 수 있다.

 

- 문제를 재구성하고 해결책을 설계하는 것은 반복적인 발견의 과정이다.

 

6-2. Data Understanding

- Data

    - 세운 해결책들에 이용가능한 원상태의 데이터셋이 존재한다.

    - (ex) a customer database, a transaction database, a marketing response database

 

- 각 데이터의 강점과 한계점을 이해한다.

    - 문제와 완벽하게 알맞는 데이터는 거의 존재하지 않는다.

    - 각 데이터로 할 수 있는 것과 없는 것을 찾고, 해당 데이터로 문제를 해결할 수 있을지 생각한다.

    - (ex) classification 을 하기 위해서는 라벨이 존재하는 데이터가 필요하다. 

 

- 데이터에 더 투자가 필요한지 결정한다.

    - 몇몇 데이터는 무료이지만, 몇몇 데이터는 얻기 위해 노력이 필요하거나 돈을 지불해야 한다.

 

6-3. Data Preparation

- 데이터를 정리해서 보다 유용한 형태로 변환한다.

    - 몇몇 데이터 분석 툴들은 특정한 형태의 데이터만 요구하기 때문이다.

 

- 일반적인 예시들

    - converting data to tabular format : 테이블 형식의 데이터로 변환

    - removing or inferring missing values : 결측치 제거하거나 유추

    - converting data to different types : (ex) 'male', 'female' → 0, 1  타입 변경 

    - normalizing or scaling numerical values : (ex) [-100, 100] → [0, 1]  범위 조절

    - cleaning data : (ex) Age : 999 → ? 데이터 정리

 

- 데이터 마이닝 결과의 질은 이 단계에 달려있다.

    - (ex) 결측치, 비정상 값, 정규화되지 않은 값

 

6-4. Modeling

- 가장 주요한 단계이다.

 

- output

   - 데이터의 규칙을 나타내는 모델이나 패턴의 일종을 생성한다.

 

- 데이터 마이닝의 근본적인 아이디어를 이해하는 것이 매우 중요하다.

    - 존재하는 데이터 마이닝 기술과 알고리즘을 이해하자.

 

6-5. Evaluation

- 데이터 마이닝 결과를 엄격하게 평가한다.

    - 다음 단계로 넘어가기 전에 그 결과가 유효하고 신뢰할 수 있다는 확신을 얻어야 한다.

 

- Examples

    - 모델의 예측 정확도 추정 (ex. 90%?)

    - training data를 넘어서는 모델의 보편성 확인 (overfitting 되지 않았는지)

    - 허위 경보의 비율 추정

 

- 결과를 즉각적으로 적용하는 대신, 일반적으로 통제된 상황에서 모델을 먼저 테스트하는 것이 바람직하다.

    - 그것이 더 쉽고 저렴하고 빠르고 안전하다.

 

- 데이터 사이언티스트는 그 모델과 평과 결과를 다른 데이터 사이언티스트들 외에 이해관계자들에게 쉽게 설명해야 한다.

    - 매니저, 고위 관계자, 프로그래머 등

 

 

6-6. Deployment

- 실제 상황에 데이터 마이닝 결과 (시스템)을 적용한다.

 

- 일반적인 시나리오

    - 새로운 예측 모델이 구현된다.

    - 그 모델은 기존의 정보 시스템과 통합된다.

 

- 많은 경우

    - Data Science Team : 프로토타입을 제작하고 평가한다.

    - Data Engineering Team : 모델을 생산 시스템에 적용한다.

 

- 적용 이후, 과정은 첫번째 단계로 되돌아간다.

    - 이 과정을 통해 얻은 통찰력과 경험을 통해 더욱 개선된 해결책을 제시할 수 있다.

 

7. Other Analytics Techniques & Technologies

- 데이터 마이닝 외에도 데이터 분석을 위한 다양한 기술들이 있다.

    - 통계학, 데이터베이스 시스템, 머신러닝 등

 

- 이러한 기술들을 익혀두는 것이 중요하다. 

    - 그것들의 목표가 무엇인지, 어떠한 역할을 하는지, 그것들의 차별점이 무엇인지

 

- 데이터 과학자에게 중요한 기술은 어떤 종류의 분석 기술이 특정한 문제를 해결하는데 적합한지 인지할 수 있는 것이다.

 

7-1. Statistics 통계학

- 분석의 바탕이 되는 많은 지식들을 제공한다.

 

- Examples

    - Data Summary (means, median, variance 등)

    - Understanding different data distributions

    - Testing hypotheses : 가설 테스트

    - Quantifying uncertainty : 불확실성 증명

    - Measuring correlation : 연관관계 측정

 

- 많은 기술들이 데이터에서 도출하는 모델이나 패턴들은 통계학에 근본을 두고 있다.

 

7-2. Database Querying

- Database system

    - 데이터 삽입, 질의 (쿼리), 업데이트 및 관리 할 수 있는 소프트웨어 응용 프로그램 

 

- Database query

    - 데이터나 데이터의 통계에 대한 특정한 요청

    - 데이터를 통해서 얻고자 하는 질문

        - (ex) 지정된 데이터 검색, 정렬, 요약 통계량 계산

    - 기술적 언어로 공식화되고 데이터베이스 시스템에 질문을 제기함

        - (ex) SQL (Structured Query Language)

SQL문 예시

 

- Data science vs. databases technologies

    - 데이터 사이언스에서 데이터베이스 시스템에 저장된 관심있는 데이터를 찾거나 조사하기 위해 데이터베이스 기술을 사용할 수 있다.

       

 

7-3. Machine Learning

- 컴퓨터 시스템이 명시적인 프로그래밍 없이, 데이터를 가지고 학습할 수 있는 능력을 제공하는 것이다.

    - 인공지능의 한 분야이다.

 

- 모델을 개발하고 모델 성능을 향상시키는 데에 데이터를 활용한다.

    - (ex) decision tree, artificial neural networks (deep learning), support vector machines, clustering, bayesian networks,...

 

- 하지만,이들 사이의 경계가 모호해졌다.

 

- 데이터 마이닝과 머신러닝은 긴밀히 연결되어있다.

    - 데이터 마이닝의 한 분야가 머신러닝으로 파생되기 시작하였다.

    - 데이터 마이닝은 머신러닝의 한 가지이다.

        - KDD (Knowledge Discovery and Data mining)

        - 둘 사이에 기술과 알고리즘은 공유된다.

    - 데이터로부터 유용하고 유익한 패턴을 찾아낸다.

 

- 그럼에도 불구하고 머신러닝은 성능향상, 인지능력 향상에 더욱 집중되어 있다.

    - (ex) robotics and computer vision

    - (ex) agent가 이 환경에서 학습된 지식을 어떻게 활용하는가

 

- 데이터 마이닝은 데이터로부터 패턴, 규칙을 찾는 것에 더욱 집중되어 있다. 

    - 상업적 응용 프로그램 및 비즈니스 문제에 특히 활용된다.

 

8. Examples of Applying These Techniques

- "어떤 고객이 가장 수익성이 있는가?" : Database systems (profiable can be calculated from existing data., not predict)

- "수익성이 있는 고객과 평균의 고객들 간에 정말 차이가 있는가?" : Statistics (hypothesis testing)

- "하지만 이 고객들은 정말 누구인가? 특징화 할 수 있는가?" : Data mining (profiling)

- "어떤 특정한 새로운 고객이 수익성이 있을까? 얼마나?" : Data mining (classification, regression)

수업 출처) 숙명여자대학교 소프트웨어학부 수업 "데이터사이언스개론", 박동철 교수님

 

1. Intro

 

데이터 사이언스는 다양한 형태로부터 얻은 데이터로 지식이나 인사이트를 도출하는 융합적인 학문이다.

크게 컴퓨터 사이언스, 수학과 통계학, 경영학적 지식이 요구된다. 

 

데이터 엔지니어는 데이터를 다루기 위한 소프트웨어와 시스템을 디자인하고 개발한다. 프로그래밍과 데이터베이스 지식을 필요로 한다. 가장 중요한 것은 데이터를 분석하기 전 전처리하는 과정을 맡는다는 것이다.

 

통계학자는 통계학적 이론과 방법을 실생활에 적용하여 문제를 해결한다. 통계학과 수학적 지식을 필요로 한다.

 

데이터 사이언티스트는 쉽게 말해서 둘 다 하는 사람이다. 분석적이고 기술적인 능력을 통해서 데이터로부터 인사이트를 도출한다. 위에서 말했듯이 프로그래밍, 통계, 경영학적 이해를 바탕으로 한다.

 

데이터 사이언티스트가 주로 필요로 하는 기술은 데이터 분석, 통계 분석 패키지 (R), 파이썬, 데이터 모델링, 머신러닝 등이다. 

주로 IT & 서비스 직군에서 근무하며, 교육이나 금융 분야에서도 데이터 과학자를 필요로 한다.

 

2. Data Science

 

최근 경영 산업은 전반적으로 데이터를 수집하는 능력을 가지고 있다. 

전략, 제조, 마케팅, 공급망 관리 등 모든 경영학적 측면에서 데이터 수집을 필요로 하고 있다. 

 

이제 거대한 양의 데이터가 생산되면서 대부분의 기업들은 경쟁우위로써 데이터 수집에 집중하고 있다.

과거에는 통계학자와 분석가가 데이터셋을 수작업으로 탐색했다면, 이제는 데이터의 양이 커지면서 수작업으로는 분석을 하기 어려워졌다.

컴퓨터와 네트워크가 중요해진 것이다.

이를 통해 데이터 과학의 원리와 기술을 더 넓은 분야에 적용하게 되었다.

 

3. Data Mining

 

데이터 마이닝은 큰 데이터셋에서 패턴을 찾는 과정이다.

경영학적 측면에서는 주로 고객의 행동을 분석하는 데 활용한다. 

고객의 패턴을 분석하여 타겟 마케팅, 온라인 광고, 추천, 신용 분석, 사기 탐색 등에 적용할 수 있다.

 

4. Data Science vs. Data Mining

 

데이터 사이언스는 데이터에서 지식을 도출하도록 하는 일련의 근본적인 원칙이다.

데이터 마이닝보다 좀 더 넓은 의미로 사용된다.

 

데이터 마이닝은 데이터로부터 숨겨진 패턴을 찾는 과정으로, 보통 데이터 수집 및 가공 이후 첫번째로 수행되는 과정이다.

즉, 데이터 마이닝 기술은 데이터 사이언스의 근본 원리를 실체화한 것이라고 볼 수 있다.

 

데이터로부터 지식을 도출할 때에는 자명하지 않으면서 수치적으로 도출되어야 한다. 

예를 들면 허리케인이 지나간 후 생수의 판매율이 20% 증가했다던가, 어떠한 과자가 기존보다 7배 많이 팔렸다던가 하는 형식으로 말이다.

 

또 한 가지 유명한 예시는 미국의 통신사 예시이다.

제한된 예산으로 고객이 다른 통신사로 넘어가는 것을 막을 방법을 찾아야 한다.

미국의 통신사 MegaTelco는 일반적으로 휴대폰 고객의 20%는 계약이 만료되면 다른 통신사로 떠나간다.

이제 휴대폰 시장이 포화되었기 때문에 통신사들은 고객을 유지하려고 노력하고 있다. 

 

회사는 몇몇 고객들의 계약 만료 전에 특별 상품을 제공하려고 한다.

이때 어떻게 최대한 고객이탈을 막을 수 있도록 상품을 제공할 고객을 고를 수 있을까.

이럴때 데이터 사이언스가 활용되는 것이다.

 

5. Data-Driven Decision Making (DDD)

 

데이터기반 의사 결정은 단순한 직관보다는 데이터 분석에 기초한 의사 결정이다. 

예를 들어, 광고를 할 때 한 사람의 경험과 직관에 기반하기보다는 소비자가 다른 광고에 어떻게 반응하는지에 대한 데이터 분석에 기초한다.

 

이 DDD에 데이터 사이언스가 필요하다. 

자동화된 데이터 분석을 통해 현상을 이해하기 위한 방법, 과정, 기술 등을 제공한다.

 

통계적으로 기업이 데이터기반 의사결정을 활용할수록 생산력이 4-6% 증가한다.

 

예를 들어, 마트의 고객은 주로 본인이 가던 곳만 가기 때문에 변화를 찾기 어렵지만, 아기가 태어남을 기점으로 한 번의 변화가 일어날 수 있다. 따라서 많은 상업자들은 아기가 태어난 것을 알고 새로운 고객에게 특별 상품(할인권 등)을 제공한다. 

하지만 미국의 마트 '타겟'은 이것을 넘어서서 아기가 태어나기 전, 임신 전부터 그것을 예측하여 마케팅을 했다. 그 결과 그들의 경쟁자보다 더 높은 이익을 얻을 수 있었다.

 

점점 더 많은 비즈니스 결정이 컴퓨터 시스템에 의해 자동화되고 있다.

미리 설계된 알고리즘을 통해 거의 실시간으로 데이터를 분석하고 의사 결정을 하고 있다.

 

예를 들면, 은행과 통신사에서는 사기 범죄를 제어하고, 소매 업체에서는 상품화 결정 시스템을 자동화한다. 아마존과 넷플릭스는 추천 시스템을, 광고 회사는 실시간 광고 결정 시스템을 자동화한다. 

 

6. Data Engineering vs. Data Science

 

데이터 엔지니어링과 데이터 사이언스는 다르다.

 

데이터 엔지니어링은 데이터를 처리하는 기술로, 시스템, 소프트웨어 디자인, 개발, 유지 및 보수 등을 포함한다. 

오라클과 같은 데이터 베이스 시스템과 하둡, 스파크와 같은 빅데이터 플랫폼 등의 소프트웨어를 개발하고 유지한다.

이들을 통해서 데이터 사이언스를 support한다.

 

데이터 사이언스는 앞에서도 말했지만, 데이터를 모으고 탐색하고 분석한다. 

데이터 엔지니어링 기술을 활용해서 데이터에 접근할 수 있다. 

 

7. Data Science and Big Data

 

빅데이터는 네트워크와 컴퓨팅 기술의 발달로 전통적인 데이터 처리 시스템으로 관리하기에는 너무 큰, 따라서 새로운 처리 기술이 필요한 데이터셋을 말한다.

 

빅데이터의 4가지 특징으로는 크기, 다양성, 속도, 정확성이 있다.

 

빅데이터 기술은 위의 4가지 특징 (4V)을 다룬다.

이를 위해 데이터 사이언스를 활용한다.

예시로는 Hadoop, HBase, MongoDB, Spark 등이 있다.

 

데이터와 데이터로부터 유용한 지식을 도출하는 능력은 핵심 전략 자산으로 간주되어야 한다.

이는 최상의 결과를 내기 위해 적절한 데이터와 데이터 사이언스 기술 두가지 모두에 투자해야한다는 것이다.

 

8. Data-Analytic Thinking

 

실생활 문제에 직면했을 때, 우리는 그 문제를 "분석적으로" 바라보아야 한다. 데이터가 성능을 향상시킬 수 있는지 여부와 그 방법을 정량적으로 평가해야 한다.

 

데이터 분석적 사고는 일련의 근본적인 개념과 원칙에 의해 가능해진다. 시스템적인 틀(framework) 을 구조화해야 한다.

 

데이터 분석적 사고를 통해서 다른 사람들과 능숙하게 교류할 수 있고, 데이터기반 의사 결정을 향상시킬 수 있으며 데이터 중심의 경쟁 위협을 바라보는 사고를 기를 수 있다.

 

최근 산업, 기업에서는 수익을 높이고 비용을 절감하기 위해 데이터 사이언스 팀을 만들고 있고, 주요 전략 요소에 데이터 마이닝을 사용하고 있다.

 

데이터 사이언티스트가 아니더라도 매니저, 마케터, 경영 전략가 등의 직업에서도 데이터 사이언스를 활용한다. 

 

9. Example of Fundamental Concepts

 

1) 자료에서 유용한 지식을 추출하기 위해 체계적인 절차를 밟는다.

 

체계적인 절차란 명확히 정의된 단계를 말한다. (ex) 문제 분석 -> 모델링 -> 면밀한 평가

이러한 과정은 데이터에 대한 우리의 생각을 구조화하기 위한 틀을 제공한다.

 

2) 대량의 데이터에서 관심있는 개체에 대한 정보 속성을 찾는다.

 

즉, 우리에게 필요한 정보를 제공하는 변수를 찾는 것이다.

 

3) overfitting을 피한다.

 

모델링을 하는 이유는 새로운 데이터를 "예측"하기 위함이다. 

overfitting된 모델은 특정 training data에만 적합할 뿐, 새로운 데이터에 대해서는 좋은 성능을 낼 수 없다. 

 

4) 마이닝 결과를 신중하고 객관적으로 평가한다.

 

우리가 도출한 결과가 얼마나 더 나은지 공식화 해야 한다.

단순히 더 낫다가 아니라 얼마나 더 나은지 보여주어야 한다.

 

10. Engineering side of Data Science

 

데이터 사이언티스트는 두가지 종류의 능력을 갖춰야 한다.

 

1) Science

- 이론적인 개념과 원칙을 실제 상황에 적용하는 능력

- ex) logistic regression, support vector machines

 

2) Technology

- 트렌드에 맞는 (상황에 맞는, 인기있는) 프로그래밍 언어와 툴을 사용하는 능력

- ex) Hadoop, MongoDB, Spark, TensorFlow 

 

최근에는 특정 종류의 소프트웨어 툴에 익숙하지 않은 데이터 과학자를 상상하기 힘들다. 

그럼에도 우리는 기술보다 과학에 조금 더 집중해야 한다. 우세한 기술들은 빠르게 바뀌기 때문이다.

+ Recent posts