데이터 품질 평가

4차산업혁명 시대가 도래하며 데이터에 대한 중요성이 점점 높아지고 있습니다. 그리고 이런 데이터의 중요성과 함께 높아지는 것이 데이터의 품질에 대한 사용자의 요구입니다.

데이터의 품질은 요즘 같이 경쟁이 심화되는 사회에서 정확한 데이터에 의한 신속한 의사결정이 필요하기 때문에 많은 양질의 데이터를 확보하는 것은 불확실한 미래를 예측하는 중요한 자원이됩니다.
그러나 반대로 어떤 의사결정에 활용했던 데이터가 품질이 낮을 경우 혹은 검증되지 않았을 경우에는 이 데이터를 통해서 했던 의사결정 역시 잘못될 확률이 높아질 것이고 이로 인한 리스크 역시 크다고 할 수 있습니다.

https://reciprocitylabs.com/resources/what-is-risk-mitigation/

데이터의 중요성은 수전부터 그 중요성에 대한 논의가 계속되어 왔음에도 불구하고 데이터를 사용하는 수요자의 입장에서는 여전히 다양한 불만이 제기되어 왔습니다.
그 대표적인 것이 데이터의 중복, 데이터의 누락, 데이터의 관계나 구조의 높은 복잡도라고 할 수 있습니다.

그렇다면 왜 이러한 문제들이 발생하게 되는 것일까요?

몇가지 이유가 있겠지만 데이터를 생산하는 주체의 낮은 업무이해, ERD와 같은 관리 문서의 부재, 데이터 입력의 오류, 복잡한 입력 프로세스, 예외가 많은 업무, 예외 처리의 부재와 가장 중요하다고 할 수 있는 체계적이지 않은 데이터 운영 조직 등… 다양한 곳에서 데이터 품질에 대한 이슈를 찾을 수 있겠습니다.

그렇기 때문에 데이터 품질활동을 위해서는 데이터의 생산, 데이터의 교환/축적, 정보생산, 정보활용의 각 부분에 대하여 Life-Cycle을 이해하고 단계별 품질관리 활동을 수행해야 합니다.

그렇다면 이런 활동을 하기 위한 “데이터 품질관리 방법은 어떤것이 있을까?”에 대한 질문을 할 수 있을 것입니다. 먼저는 데이터 품질 관리를 위한 분석작업이 선행되어야 합니다. 그리고 이 분석 방법은 크게 InSide-Out, OutSide-In의 두가지 방법이 있습니다.

Inside-Out 방식은 “데이터 자체를 분석함으로 품질을 관리하는 형태의 접근법”이라고 할 수 있습니다. 즉 데이터 그 자체에 주목하고 분석하는 기법으로 데이터의 컬럼분석, 패턴분석, 코드분석, 중복분석, 상관분석, 참조무결성 분석 등이 그것이라고 할 수 있습니다.

반면 Outside-In 방식은 “외부의 비지니스, 서비스 품질 이슈”로 부터 접근하는 방법으로 데이터 규칙, 업무 규칙, 각종 비지니스룰 기반으로 부터의 접근하는 방법입니다.

이 두가지 방법 중에 더 시간이 걸리고 어려운 것은 Outside-In 방식입니다. 이것은 업무프로세스에 대한 이해가 있어야지만 가능한 분석방법이기 때문입니다.

다음으로는 데이터의 품질관리 프로세스에 대해서 생각해보겠습니다.
품질관리는 정의에 따라 차이가 있겠지만 크게 6가지 단계로 정의할 수 있습니다.

데이터 품질관리 프로세스
  • 진단정의 단계(Define) : 품질 이슈에 대한 수요 및 현황을 조사하여 진단 대상을 선정하고 방향을 정의하는 단계
  • 품질진단(Measure) : 품질 진단 대상에 대한 상세 수준의 품질 진단 수행 계획 수립 후 영역별 진단 실시
  • 결과분석(Analyze) : 오류원인 분석, 업무 영향도 분석을 통해 개선과제 정의
  • 개선(Improve) : 상세 수준의 품질 개선 계획 및 영역별 품질 개선 수행
  • 통제(Control) : 목표 대비 결과 분석, 평가를 통한 품질관리 수행

이런 데이터의 품질관리를 통해서 사용자는 데이터의 제공에 대한 안정성, 데이터 신뢰성, 데이터 활용의 용어성 등에 대한 보장을 받게 됩니다. 만일 데이터를 생산만하고 그 품질에 대한 보장이 되지 않는다면 데이터를 활용하여 의사결정을 한다는 것은 큰 위험을 감수해야만 할 것입니다.

반면 이러한 품질관리를 지속적으로 수행한다면 사용자는 데이터 품질에 대한 정확성, 일관성, 유용성, 접근성, 적시성, 보안성 등의 조건을 만족하게되고 데이터를 활용한 의사결정이나 인공지능 모델에 대한 신뢰도를 높일 수 있습니다.

Word2Vec 시각화

Word2Vec은 간단을 간단히 말하면 “문장안에 있는 여러 단어들을 벡터 형태로 표현하는 것” 말 그대로 Word to Vector라고 할 수 있습니다. 워드라는 말은 쉽게 이해할 수 있지만 벡터(Vector)는 어떤 뜻일까요?

물리학이나 수학에서 약간씩 차이가 있지만 공통적으로 어떤 공간에서 위치와 방향성을 가지는 값을 표현하는 것이라고 할 수 있습니다. 그러니까 문장에 많은 Word를 어떤 공간에 위치값을 표시할 뿐만아니라 이 값들이 어떤 방향성이 있는지를 표시하는 기법이 Word2Vec이라고 하겠습니다.

위의 이미지는 word2vec의 가장 유명한 그림 중에 하나입니다. 각 단어들을 보면 어떤 방향성이 있고 숫자 값을 가지고 있습니다. 그렇기 때문에 유사도를 계산 할 수도 있고 각 단어의 관계에 대한 연산이 가능합니다.
예를 들어서 “KING-MAN+WOMAN=QUEEN”이라는 관계가 나온다는 것이죠.
또 “한국-서울+도쿄=일본”라는 관계를 추출할 수 있습니다. 아래 링크를 방문해보시고 다양한 케이스를 테스트해보시기 바랍니다.
https://word2vec.kr/search/

이것은 전통적인 방법인 One-Hot-Encoding을 통해서 단어를 표현하는 것의 문제점을 극복할 수 있는 아주 유용한 방법입니다. 이렇게 단어들을 벡터로 바꾸는 것을 워드 임베딩(Word-Embedding)이라고 하고 그중에서 가장 대표적인 모델이 Word2Vec 모델로 해당 단어와 함께 자주 등장하는 단어는 비슷한 단어일것이라는 가정으로 출발합니다.

본 예제는 Word2Vec의 원리와 이론을 소개하는 것은 아니고 실제로 단어를 2차원 공간에 표시하는 방법에 대한 예제코드이기 때문에 해당 이론이 궁금하신 분들은 인터넷에 공개된 많은 예제들이 있으니 참고해보시기 바랍니다.

예제를 실해하기 위해서 먼저 필요한 라이브러리를 import합니다.
분석할 데이터는 인터넷 쇼핑몰의 마우스를 구매한 후에 남긴 후기들을 모은 것입니다. 예를 들어 제품의 이름을 선택했을 경우에 해당 단어와 가장 거리가 가까운 단어들이 긍정의 단어들이라면 제품의 평가가 좋을 것일테고 반대로 제품이 부정적인 단어들과 거리가 가깝다면 반대의 경우라고 생각할 수 있겠습니다.

import pandas as pd
import numpy as np

df_r = pd.read_excel("./mouse_review.xlsx")
df_r.head()

파일을 읽어온 뒤에 pandas의 head() 함수로 상위 5개의 데이터를 추출해봅니다.
데이터는 사용자, 작성일, 리뷰 내용, 별점, 제품명 정보가 있습니다.

이번에 사용할 text 정보는 리뷰 내용입니다. 리뷰에 보면 여러가지 특수기호, 영문자 등이 있기 때문에 정규식을 통해서 한글 외에 나머지 데이터를 걸러냅니다. 걸러낸 데이터는 review_train 컬럼을 만들어서 원본 데이터와 별도로 저장해둡니다.

df_r['review_train'] = df_r['review'].str.replace("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]","")
df_r.head()

1차로 정규화를 끝낸 텍스트 데이터를 통해서 문장을 형태소별로 분리해줍니다. 또 사용하지 않는 단어들의 사전을 모아서 불용단어를 걸러냅니다. 분석하고자 하는 상황에 맞춰서 불용어를 등록해줍니다.

from konlpy.tag import Okt

stop_words = ['가','요','변','을','수','에','문','제','를','이','도','은','다','게','요','한','일','할','인데','거','좀','는데','ㅎㅎ','뭐','까','있는','잘','습니다','다면','했','주려','지','있','못','후','중','줄']

okt = Okt()
tokenized_data = []
for sentence in df_r['review_train']:
    temp_X = okt.morphs(sentence, stem=True) # 토큰화
    temp_X = [word for word in temp_X if not word in stop_words] 
    tokenized_data.append(temp_X)

이제 시각화를 위한 준비를 해줍니다. 시각화는 matplolib을 사용합니다.
한글화를 위해서 폰트를 설정해줍니다.
본 예제는 Mac OS환경에서 테스트 되었기 때문에 폰트의 위치는 Window 사용자와 틀릴 수 있으니 테스트 환경에 맞게 폰트 정보를 변경해줍니다.

import matplotlib.pyplot as plt
from matplotlib import font_manager, rc
font_name = font_manager.FontProperties(fname='/System/Library/Fonts/Supplemental/AppleGothic.ttf').get_name()
rc('font', family=font_name)

리뷰 텍스트의 정보들을 간단히 표시해줍니다.
학습에 필요한 단계는 아니니 데이터에 대한 정보를 보고자 하지 않는다면 그냥 넘어가셔도 되겠습니다. 본 예제에 사용된 데이터는 대부분 길이가 0~50글자 사이의 비교적 짧은 문장들이라는 것을 알 수 있습니다.

print('리뷰의 최대 길이 :',max(len(l) for l in tokenized_data))
print('리뷰의 평균 길이 :',sum(map(len, tokenized_data))/len(tokenized_data))
plt.hist([len(s) for s in tokenized_data], bins=50)
plt.xlabel('length of samples')
plt.ylabel('number of samples')
plt.show()

이제 Word2Vec 모델을 생성할 차례입니다.
Word2Vec 모델은 가장 잘 알려진 gensim 라이브러리를 활용해보겠습니다.

사용한 파라메터의 자세한 정보는 아래 링크를 참조해보시기 바랍니다.
본 모델은 좌우 5개의 단어를 참조하는 100차원의 워드 벡터를 만드는 모델로 cobow 알고리즘을 사용하고 최소 5번 이하로 등장하는 단어들은 제외하겠습니다. worker는 thread의 갯수로 테스트하는 하드웨어의 성능에 따라서 조정할 수 있습니다.
https://radimrehurek.com/gensim/models/word2vec.html

from gensim.models import Word2Vec
model = Word2Vec(sentences = tokenized_data, size = 100, window = 5, min_count = 5, workers = 4, sg = 0)

학습 데이터가 많지 않기 대문에 학습 시간은 오래 걸리지 않습니다.
학습이 끝난 후에 단어들을 추출해서 벡터 리스트를 생성합니다. 해당 리스트 하나를 출력해보면 아래와 같은 데이터가 표시됩니다.

vocabs = model.wv.vocab.keys()
word_vocab_list = [model.wv[v] for v in vocabs]
array([ 0.45729467, -0.45482287,  0.2776271 , -0.38435346,  0.4311736 ,
       -0.36617622,  0.12129851, -0.309033  , -0.09569103, -0.27311006,
        0.28018764, -0.13276236,  0.13590969,  0.0521839 , -0.01882668,
        0.13234554, -0.02577238,  0.43111804, -0.6007069 ,  0.52846146,
        0.01065135, -0.20410554,  0.08504212, -0.5189065 ,  0.06219423,
       -0.10900757,  0.19578645, -0.01295294, -0.20757432, -0.17270625,
        0.08728364,  0.4751571 , -0.06208701, -0.3829262 ,  0.4810491 ,
       -0.27205822, -0.16547562, -0.2804698 ,  0.1357591 ,  0.16740464,
        0.53618526, -0.17420012,  0.06363445,  0.655636  ,  0.05952126,
       -0.6312642 ,  0.11448789, -0.00824977, -0.26018238, -0.33553734,
        0.18489622,  0.03913857, -0.5856825 , -0.08111028,  0.6696569 ,
        0.4201213 , -0.2061224 , -0.03785964, -0.0813726 ,  0.0297378 ,
       -0.5556496 , -0.0006753 ,  0.25876167,  0.08983239, -0.10351149,
        0.24005203,  0.21328437,  0.0797505 , -0.23059952, -0.32846287,
       -0.0017608 ,  0.51077896,  0.36693272,  0.2767188 , -0.47870687,
       -0.3036568 , -0.06708886, -0.4789917 , -0.08152916,  0.19817959,
        0.07031752, -0.34857494,  0.5963662 ,  0.02050934,  0.29983994,
        0.07854129,  0.40096822,  0.00098353, -0.26964054, -0.12954848,
        0.33181033, -0.07866482,  0.40206903, -0.37808138, -0.10669091,
       -0.15223539, -0.01180514, -0.13499472,  0.31345636,  0.08265099],
      dtype=float32)

Word2Vec에서 제공하는 함수인 most_similar()를 통해서 입력하는 단어와 가장 가까운 단어 정보를 표시해봅니다. “클릭”과 가장 가까운 단어는 “버튼”,”소리” 등의 순서로 각 단어간의 연관성이 매우 높다는 것을 알 수 있습니다.

아마도 마우스라는 제품의 특징상 클릭이라는 단어와 함께 버튼, 소리, 게임, 느낌 등의 단어가 많이 등장했다는 것을 알 수 있습니다.

print(model.wv.most_similar("클릭"))
[('버튼', 0.9999016523361206), ('소리', 0.9998948574066162), ('게임', 0.9998838305473328), ('느낌', 0.9998810291290283), ('아니다', 0.9998774528503418), ('되다', 0.9998745918273926), ('이나', 0.9998740553855896), ('만', 0.9998738765716553), ('재질', 0.9998738169670105), ('누르다', 0.9998728036880493)]

이제 각 단어와의 관계를 그래프로 나타내보겠습니다. 해당 데이터는 100차원의 데이터이고 그려보고자 하는 것은 2차원에 표시되는 그래프이기 때문에 차원을 축소할 필요가 있습니다.

잘알려진 차원축소 알고리즘으로 PCA기법이 있습니다.

PCA(Principal Component Analysis)는 차원축소(dimensionality reduction)와 변수추출(feature extraction) 기법으로 널리 쓰이고 있는 기법으로 데이터의 분산(variance)을 최대한 보존하면서 서로 직교하는 새 기저(축)를 찾아, 고차원 공간의 표본들을 선형 연관성이 없는 저차원 공간으로 변환하는 기법입니다. 
https://ratsgo.github.io/machine%20learning/2017/04/24/PCA/

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
xys = pca.fit_transform(word_vocab_list)
xs = xys[:,0]
ys = xys[:,1]

#plt.figure(figsize=(10 ,10))
plt.scatter(xs, ys, marker = 'o')
plt.xlim(0,1), plt.ylim(0,0.01)
for i, v in enumerate(vocabs):
    plt.annotate(v, xy=(xs[i], ys[i]))

해당 기법을 통해서 아래와 같은 그래프를 그렸습니다. 해당 그래프는 전체 그래프에서 일부 구간(xlim, ylim)을 표시한 것으로 전체 데이터는 아닙니다.

높은 차원의 데이터를 평면으로 축소하면서 데이터의 구간이 많이 겹치는 것을 알 수 있습니다. 이러한 문제는 데이터를 더 높은 차원의 공간(3차원)에 표시한다던가 아니면 의미 없는 데이터들을 추출해서 데이터의 수를 줄여서 표시할 수도 있습니다.

Abstract Class와 Interface Class

추상클래스(Abstract Class)와 인터페이스(Interface) 대해서 알아보겠습니다.

추상화에 대한 이야기는 이미 객체지향언어의 특징에서 다뤘기 때문에 간단히 설명하고 넘어간 후 추상클래스를 어떻게 만드는지에 대해서 알아보겠습니다.

추상화는 객체에서 어떤 공통된 요소들(Attribute, Method)를 추출해서 클래스를 만드는 것이라고 했습니다. 그렇다면 추상화된 클래스는 일반 클래스와는 그 성격이 어떻게 다를까요?

일반 클래스는 new 라는 명령어를 통해서 객체를 만들어 낼 수 있지만 추상클래스는 new 라는 명령어를 통해서 객체를 만들어 낼 수 없고 상속이라는 과정을 통해서 객체를 구현해야 합니다. 자바에서는 이러한 클래스를 일반 클래스와 차별적으로 abstract class라고 표현합니다. 참고로 일반 클래스는 abstract를 붙이지 않습니다.

메서드도 마찬가지입니다. 일반 메서드는 구현체가 존재하지만 추상 메서드는 구현체가 존재하지 않고 이를 상속 받는 클래스에서 구현할 수 있도록 선언만 해줍니다. 이렇게 선언만 한 메서드를 추상메서드라고 표현하며 메서드의 앞에 abstract를 붙여줍니다.

클래스를 선언할 때에 하나라도 추상메서드가 포함된 클래스는 추상클래스가 됩니다.

그러다면 추상 클래스는 왜? 그리고 언제 사용하는 것일까요?

만약 게임 캐릭터를 만든다고 가정해봅니다.
캐릭터는 여러가지가 있겠죠. 기사, 궁수, 마법사, 힐러 등…

이러한 캐릭터에서 공통적인 요소들을 만들어 낼 수 있을까요?

캐릭터의 체력, 스피드, 키, 체중, 달리기, 걷기, 점프 등의 요소들을 공통적인 요소로 만들 수 있을 것입니다. 그리고 그 요소들을 Unit 이라는 추상클래스를 만들고 모든 게임 캐릭터는 해당 유닛 클래스를 상속하도록 만들 수 있을 것입니다.

public abstract class Unit {
	private String name;
	private int point_x;
	private int point_y;
	private int stamina;
	private int height;
	private int weight;
	
	
	public Unit(String name, int x, int y) {
		this.name = name;
		this.point_x = x;
		this.point_y = y;
	}
	
	public void move(int x, int y) {
		point_x += x;
		point_y += y;
	}
	
	public String getName() {
		return name;
	}
	
	public int getPointX() {
		return point_x;
	}
	
	public int getPointY() {
		return point_y;
	}
	
	public abstract void weapon();
	public abstract void jump();
	public abstract void run();
	
}

게임 프로젝트의 설계자는 이제 각 캐릭터 개발자에게 Unit 클래스를 상속해서 각기 캐릭터를 만들라고 지시 할 수 있을 것입니다. 그렇게 된다면 각 개발자들은 각 캐릭터마다 체력, 스피드, 키, 체중 등의 요소를 지정하고 달리고, 걷고, 점프 하는 동작들을 구현할 수 있을 것입니다.

이런 설계는 개발하면서 굉장히 중요한 부분입니다.
만약 이런 부분이 없이 각가의 개발자에게 캐릭터를 만들어 내라고 한다면 아마도 많은 혼란이 있을 것입니다. 만들어진 코드도 동일한 행위를 하는 다양한 이름으로 구현될 것입니다. 또 각각의 객체에 정의하는 초기값도 역시 프로그래머 각각 정의하게 될테니 개발하는 과정에서 많은 시간과 자원이 낭비될 것입니다.

이러한 것을 방지하기 위해서 추상클래스를 정의하고 이를 상속하도록 하는 것이 프로그램의 통일성을 주는데 용이합니다.

그렇다면 인터페이스는 언제 사용하는 것일까요?

인터페이스는 추상클래스와 비슷한 점이 많습니다. 다만 차이가 있다면 인터페이스는 공통적인 객체의 정의라기 보다는 행위에 집중합니다. 즉 구현체가 같은 행위를 한다는 것을 보장한다는 것입니다.
그렇기 때문에 추상클래스는 상속이라고 표현하지만 인터페이스는 행위의 구현이라고 표현합니다.

예를 들어서… 다른 언어를 한국어로 번역을 하는 프로그램을 만들어 본다고 생각해봅니다. 설계자는 번역의 행위에 집중할 것입니다. 다른 언어를 한국어로 번역하는 행위, 한국어를 다른 어로 번역하는 행위 이렇게 행위를 정의할 것입니다. 그리고 이 두행위를 하는 인터페이스 클래스를 만듭니다.

public interface TranslateInterface {
	public String translate2korean(String sentence);
	public String translate2foreignlanguage(String sentence);
}

이 인터페이스 클래스에는 두가지 역할을 하는 메서드 외에는 존재하지 않습니다. 그리고 이 파일을 다른 개발자들에게 나눠주고 이 파일을 통해서 인터페이스의 미구현 부분을 구현(implement)하라고 할 것입니다. 개발자들은 이 파일을 통해 각각 번역기를 구현할 때에 어떻게 구현해야 하는가에 대하여 고민할 필요가 없이 translate2korean, translate2foreignlanguage 메서드를 구현할 것이며 방식 역시 각가의 메서드에 source 문장을 주고 target 언어로 리턴하는 부분을 구현하면 됩니다.

지금까지 설명드린 것처럼 인터페이스와 추상클래스는 상속(혹은 구현)하는 과정을 통해서 프로그램을 더 명식적으로 만들어주고 협업시에 효율을 높여줍니다. 코드의 유지보수나 재사용에도 큰 장점이 있습니다.

객체지향 언어의 특징

캡슐화(Encapsulation)

보통의 사용자가 스마트폰을 사용하는데는 스마트폰의 구조와 작동원리에 대한 지식 보다는 사용에 필요한 기능이 무엇인지 또 어떤 때에 어떻게 사용하는지에 대해서만 알면 됩니다. 터치를 했을 경우에 소프트웨어가 어떻게 동작하고 하드웨어가 어떻게 동작하는지에 대해서 몰라도 사용하는데는 지장이 없습니다.

만약 기기가 전부 오픈되어 있어서 사용자가 조작하지 말아야 할 부분까지 만질 수 있도록 되어 있다면 친절하다기 보다는 사용자의 실수나 기기의 고장의 원인이 될 것입니다.

프로그램도 마찬가지로 사용자가 내부의 주요 소스코드에 접근하는 것은 오히려 좋지 않습니다. 많은 변수들이 내부에서 생성되고 소멸되는데 그런 민감한 변수들을 조작하는 것은 프로그램 오류의 원인이 됩니다.

개발자는 이러한 것을 미연에 방지하도록 주요 로직과 변수들을 숨기고 오직 사용자가 최소한의 프로그램 동작에 필요한 것들만 접근하고 사용할 수 있도록 하는 것이 좋습니다.

이러한 것을 객체지향의 캡슐화 혹은 정보은닉이라고 표현합니다.

추상화(Abstraction)

추상화는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념이나 기능을 간추려 내는 것이라고 할 수 있습니다.

예를 들어 여러 브랜드의 다양한 자동차가 있습니다. 모든 자동차들은 그 종류에 따라서 크기, 무게, 넓이, 높이 등의 차이가 있습니다. 그러나 이러한 차이에도 불구하고 모든 자동차들을 잘 관찰하면 몇가지 공통된 특징을 찾아 낼 수 있습니다.

스티어링 휠, 바퀴, 주행장치, 제동장치, 문, 헤드라이트 등 공통점을 찾아내서 하나의 기본 개념을 만듭니다. 이렇게 만들어진 객체를 추상화된 객체라고 할 수 있습니다. 추상화된 객체는 “IS A”관계로 나타낼 수 있습니다. 즉, “기아자동차의 K7은 자동차이다”라는 관계를 만들어 낼 수 있습니다. 이와 마찬가지로 “현대자동차의 그랜저는 자동차이다”라는 관계가 맺어집니다. 이렇게 하면 자동차로부터 추상화된 객체는 “IS A” 관계가 됩니다.
그러나 “자동차는 K7이다.” 관계는 좀 어색합니다.

이 개념은 간단하지만 추상화의 매우 중요한 개념입니다.

같은 개념을 이전 프로그램 코드로 본다면 사람의 모든 특징을 조합하여 Human이라는 추상클래스를 만들었다고 생각해 봅니다. 그리고 Man 이라는 인터페이스 클래스를 만듭니다. 이제 Human + Man = Adam 이라는 클래스를 만들었습니다.

이 Adam이라는 객체를 통해서 “David”, “John” 객체를 만듭니다.
이렇게 하면 David ≠ John 이지만 Human = David, Human = John의 관계는 성립하게 됩니다.

public class HumanDemo {
	public static void main(String[] args) {
		Adam david = new Adam("David", 180, 80);		
		Human john = new Adam("John", 178, 75);
		Man mark = new Adam("Mark", 178, 75);
	}
}

이렇게 추상화한 객체와 이를 상속 받은 객체 사이에 “IS A”의 관계가 성립한다는 것이 추상화의 중요한 개념입니다.

상속(Inheritance)

상속은 추상화된 클래스 즉, 객체의 일반적인 특성이 정의된 클래스를 사용하여 새로운 객체를 만드는 것을 의미합니다. 단 추상화된 클래스는 반드시 추상클래스라는 의미는 아니니 혼동 없으시기 바랍니다.

이는 보통 부모-자식 클래스 혹은 상위-하위 클래스로도 이름할 수 있습니다. 앞으로는 자식 클래스로 이름하겠습니다. 부모 클래스는 super를 통해서 접근이 가능합니다.

자식클래스는 부모 클래스의 속성을 물려 받을 수 있습니다. 별도로 정의하지 않아도 되지만 필요하다면 정의할 수도 있는데 이것을 Overriding이라고 합니다.

이러한 상속의 개념을 이해하고 설계에 반영하면 객체의 재사용이나 기능 추가 혹은 유지보수 등에 큰 장점이 있습니다.

자바(JAVA)에서 상속은 extends를 통해서 가능하고 extends를 통해서는 단일 상속 밖에는 지원하지 않습니다. 그러나 interface class의 경우 implements를 통해서 다중 상속도 가능합니다.

다형성(Polymorphism)

객체지향의 특징에서 가장 이해하기 어려운 개념이 바로 다형성 개념일 것입니다. 다형성은 말 그대로 상속 받은 메서드가 자식 클래스에서 다양한 방식으로 개발 할 수 있도록 허용하는 것이다. 이 다형성이 상속과 연계되어서 자바의 높은 언어적인 효율성이 발휘된다고 할 수 있습니다.

이 다형성을 잘 이해하고 설계하면 코드도 간결해질 뿐더러 변화에도 유연하게 대처할 수 있습니다.

아래의 코드는 일반적인 형태의 코드입니다. 한국인, 중국인, 영국인 객체를 선언하고 각각 모국어를 말하는 클래스입니다. 이 세 객체는 “말한다”라는 하나의 기능을 수행하는 메소드를 가지고 있지만 각기 다른 이름으로 구현되어 있습니다.

package ch01;

public class SpeakDemo {
	public static void main(String[] args) {
		Korean k = new Korean();
		Chinese c = new Chinese();
		Briton b = new Briton();
		
		k.speakKorean();
		c.speakChinese();
		b.speakEnglish();
	}
}

class Korean {
	public void speakKorean() {
		System.out.println("한국어");
	}
}

class Chinese {
	public void speakChinese() {
		System.out.println("중국어");
	}
}

class Briton{
	public void speakEnglish() {
		System.out.println("영어");
	}
}

이제 위의 코드에서 Korean, Chinese, Brion 클래스에서 공통적인 요소를 추출하여 추상클래스를 만들고 이 세 언어의 공통적인 기능인 “speak()” 메소드를 정의하겠습니다. 이 추상클래스를 공통으로 상속 받는 객체들은 반드시 speak() 메소드를 구현해야 합니다.
그러나 이 speak() 메소드를 상속 받은 클래스는 각자의 상황에 맞춰서 해당 메소드를 다르게 구현합니다. 추상클래스에서는 정의만 하고 이를 상속하는 클래스에서 각기 다른 방식으로 내용을 구성하는 것입니다.

package ch01;

public class SpeakDemo2 {
	public static void main(String[] args) {
		Language ko = new Korean();
		Language ch = new Chinese();
		Language br = new Briton();
		
		ko.speak(); ch.speak(); br.speak();
	}
}

abstract class Language {
	public abstract void speak();
}

class Korean extends Language {
	@Override
	public void speak() {
		System.out.println("한국어");
	}
}

class Chinese extends Language {
	@Override
	public void speak() {
		System.out.println("중국어");
	}
}

class Briton extends Language { 
	@Override
	public void speak() {
		System.out.println("영어");
	}
}

위와 같은 방식으로 코딩하면 세 객체간의 관계가 명료해지고 또 다른 언어가 생성 될 때에 상속을 받고 speak() 부분만 구현하면 되기 때문에 코드가 간결해지고 재활용이 가능해집니다. 그리고 부모 객체의 메소드를 자식에서 구현할 때에 명시적으로 @Override를 어노테이션을 표기해주면 더욱 명시적입니다.

비슷한 개념으로 @Overload가 있습니다.
해당 개념은 하나의 메소드 이름을 각기 다른 매개 변수를 사용해서 호출하는 것으로 가장 많이 쓰는 예로는 System.out.print() 함수가 있습니다. 해당 함수는 String, boolean, long, int 등 매개변수를 다르게 해도 모두 동일한 print()를 사용합니다.

그러므로 오버로딩의 조건은 메서드의 이름이 같고, 매개 변수의 타입이 틀려야 합니다. 만약 Overload가 되지 않았다면 아마도 자바는 비슷한 이름의 지금 보다 훨씬 더 많은 메소드를 보유하면서 개발자를 힘들게 만들었을 수도 있을 것입니다.

객체지향의 역사와 이해

초기의 컴퓨터가 나타났을 때에 연산은 주로 배선으로 만들어 졌습니다. 이런 방식은 알고리즘의 수정이나 변경이 어려웠기 때문에 당연히 많은 문제점이 나타났고 이에 대한 개선의 노력이 이어졌습니다. 그러다가 폰 노이만(Von Neumann architecture)에 의해서 현대적인 컴퓨터 구조가 제안되면서 하드웨어, 소프트웨어로 구분되기 시작했습니다. 이후 1950년 부터 프로그램 언어가 등장하기 시작했습니다.

wikipedia.org

이후 연구자들은 소프트웨어 공학이라는 소프트웨어의 개발, 운용, 유지보수의 생명 주기 전반을 체계적으로 다르는 학물을 발전시켰습니다.
최초의 소프트웨어 공학용어를 사용한 해는 1968년으로 알려져 있습니다. 이 당시는 소프트웨어의 개발 속도가 하드웨어 개발 속도를 따라가지 못해 사용자들의 요구사항을 처리 할 수 없는 문제들이 발생되는 소프트웨어 위기(Software Crisis) 론이 등장하는 시기였습니다.
이러한 시대적인 요구사항 속에서 연구자들은 자연스럽게 이 위기를 어떻게 극복할 것인가에 대하여 그 해결책을 찾기 위해 고민했습니다.

객체지향언어도 이 당시에 최초로 등장했습니다.
최초의 객체지향 언어는 시뮬라67이었습니다. 비록 이 당시에 큰 주목을 받지 못했지만 향후 객체지향언어의 발전의 큰 영향을 주었습니다. 이후 스몰토크, 에이다 같은 프로그램들이 등장하면서 객체지향에 대한 연구가 활발하게 이뤄졌습니다.

객체지향에 대한 필요는 기존 언어의 한계에서 비롯되었다고 할 수 있습니다. 기존에는 절차식 프로그래밍(Procedural Programming)은 하위 프로그램, 서브 루틴, 메서드, 함수 등의 용어로 활용되는 프로시저를 호출하며 프로그램을 실행하는 것이었습니다.
어려운 말로 들리지만 간단히 말하면 수행되어야 할 코드를 연속적으로 정의하는 것이라고 할 수 있다.

비교적 프로그램의 구조가 간단하기 때문에 많이 사용되었지만 대규모 개발에서 생산성이 좋지 않고 프로그램의 유지보수가 좋지 않았기 때문에 여러가지 많은 문제들이 발생했습니다.

객체지향은 이러한 환경에서 출발했기 때문에 자연스럽게 프로그램 코드의 재사용성을 높여 생산성을 향상 할 수 있을까 또 어떻게 하면 프로그램의 유지보수나 기능 개선에 드는 시간과 비용을 절약할 수 있을까라는 문제 해결에 주안점을 두게 되었습니다.

그 결과 클래스(Class), 객체(Object), 메서드(Method) 를 기본 구성요소로 추상화, 상속, 다형성 등을 특징으로 가진 객체지향 언어가 등장했고 강한 응집력(Strong Cohesion)과 약한 결합력(Weak Coupling)을 통해서 기존 언어의 문제점들을 극복하고 1995년 자바가 등장한 이후 가장 강력한 언어가 되었습니다.

객체지향언어는 1905년 이후 자바가 등장하면서 많은 대중화를 이뤄냈고 자바(JAVA)는 객체지향이라는 등식을 만들어 냈습니다.

그렇다면 객체지향이란 무엇인가?
이 말은 객체와 지향이라는 두 단어의 조합으로 이뤄져있는 단어입니다.

객체(Object)는 이 세상의 존재하는 모든 것들이라고 할 수 있습니다. 그리고 지향한다는 것은 방향성을 의미한고 할 수 있습니다. 즉 객체지향이라는 것은 세상에 존재하는 객체가 어떻게 동작하고 구성되어 있는가의 원리를 프로그램으로 도입한 것이라고 할 수 있겠습니다.
그러나 객체지향은 여전히 절차적 언어의 특징을 가지고 있으며 기존의 다양한 제어문, 반복문 등을 사용합니다. 다만 기존의 개발방식에 객체라는 지향점을 코딩의 패러다임을 변화한 것이라고 생각하시면 됩니다.

예를 들어 인간이라는 캐릭터를 컴퓨터 상에 구현한다고 생각해봅니다.
(* 이것은 어떤 철학이나 의학 등의 개념이 아닌 프로그램의 예로 든것입니다.)

인간 = Attribute + Method 로 정의할 수 있습니다.
Attribute는 인간을 표현하는 여러가지 속성들 즉, 눈, 코, 입, 귀, 다리, 팔, 키, 몸무게 등이 있고 Method는 프로그램 안에서의 서다, 걷다, 눕다 등 인간의 동작이나 행위를 규정한다고 할 수 있습니다.
단, 프로그램 안에 모든 속성을 다 객체에 담을 필요는 없고 만들고자 하는 프로그램에서 목적하는 몇가지 속성과 메서드를 정의하면 됩니다. 본 예제에서는 이름과, 키, 몸무게 정도만 사용해서 human이라는 추상클래스(Attribute + Method)로 만들었습니다.

모든 인간 캐릭터는 추상클래스를 상속하게 됩니다. 그렇기 때문에 추상클래스는 이를 상속하는 객체의 대표성을 띄게 됩니다.

package ch01;

public abstract class Human {

	private String name;
	private int height;
	private int weight;
	
	public Human(String name, int height, int weight) {
		this.name = name;
		this.height = height;
		this.weight = weight;
	}
	
	public abstract void run();
	public abstract void jump();
	public abstract void walk();
	
	public void getFeature() {
		System.out.println(String.format("%s, %d, %d", name, height, weight));
	}
}

사람은 남자와 여자로 나눌 수 있기 때문에 인간의 특징 중에 남자의 특징적 요소를 다음과 같이 정의했습니다.

package ch01;

public interface Man {
	public void work();
	public void fight();
}

만약 어떤 클래스가 Human 클래스와 Man 인터페이스를 상속 받는 다면 해당 클래스는 이름, 키, 몸무게, 달리고, 뛰고, 걷고, 싸우고, 일하는 속성을 공통으로 받게 됩니다. 이것을 상속이라고 합니다.

예제는 Adam이라는 클래스를 만들고 Human, Man 등의 클래스를 상속 받았습니다.

package ch01;

public class Adam extends Human implements Man {

	public Adam(String name, int height, int weight) {
		super(name, height, weight);
	}

	@Override
	public void work() { }

	@Override
	public void fight() { }

	@Override
	public void run() { }

	@Override
	public void jump() { }

	@Override
	public void walk() { }
}

이제 상속 받은 클래스를 통해서 하나의 객체를 만들었습니다. 사실 클래스는 어떤 객체를 만드는 설계도라고 할 수 있고 이 설계도를 통해서 컴퓨러 메모리 공간에 만들어 낸 것을 객체(Object)라고 합니다. (이 이름에는 약간씩 다른 의미로 부르기도 합니다.)

package ch01;

public class HumanDemo {
	public static void main(String[] args) {
		Adam adam = new Adam("아담", 180, 80);
		Eve eve = new Eve("이브", 165, 50);
		
		adam.getFeature();
		eve.getFeature();
	}
}

이런 식으로 코딩하게 되면 인간+남자 클래스를 상속 받아 각기 다른 수많은 남자 객체를 메모리 상에 구현할 수 있습니다. 마찬가지로 인간+여자 클래스를 상속 받으면 수많은 여자 객체를 메모리 상에 구현 할 수 있습니다.

이러한 프로그램 개발 방식이 객체지향입니다. 이와 같은 방법으로 Car 클래스를 정의하고 이를 상속 받아 Bus, Taxi 등의 객체를 만들 수 있고 Animal 클래스를 정의하고 이를 상속 받아서 Cat, Dog 등의 클래스를 만들어 낼 수도 있습니다.
이렇게 개발하면 코드의 재사용성을 높일 수 있고 기존의 기능을 수정하거나 새로운 기능을 추가하거나 하기가 용이합니다.
그러나 그만큼 객체지향적 설계에 많은 시간을 들여야 합니다.

객체지향 언어는 몇가지 중요한 특징을 가지고 있습니다.

추상화(Abstraction), 캡슐화(Encapsulation), 상속(Inheritance), 다형성(Polymorphism)이 그것입니다.

다음에는 이러한 요소들에 대해서 살펴보도록 하겠습니다.

시계열 데이터의 이해

시계열 데이터는 간단히 말해서 시간별로 구성된 데이터의 집합입니다.

시계열 데이터는 기록 추세, 실시간 경고 또는 예측 모델링을 위해 분석 할 수 있습니다.

시계열 데이터는 자산 또는 프로세스가 시간이 지남에 따라 어떻게 달라지는 지를 나타냅니다. 데이터에는 타임스탬프 축이 있어 시간의 순서대로 데이터가 축적되기 때문에 이러한 데이터를 활용하면 역방향으로 사건의 케이스를 분석 할 수 있고 이를 바탕으로 앞으로 일어날 일을 예측 할 수 있게 됩니다.

https://docs.microsoft.com/ko-kr/azure/architecture/data-guide/scenarios/images/time-series-insights.png

시계열 데이터의 대표적인 예로는 센서 데이터, 주가 데이터, 클릭 스트림, 애플리케이션 로그 등이 있습니다.

  • 추세를 검색하기 위해 시간에 따라 캡처한 주가 데이터
  • 서버 성능(예, CPU 사용량, I/O 부하, 메모리 사용량 및 네트워크 대역폭 사용량)
  • 보류 중인 장비 오류 및 트리거 경고 알림을 검색하는데 사용할 수 있는 산업 장비 센서
  • 주행에 대한 위험 점수 집계를 계산하기 위한 시간별 속도, 브레이크 사용 및 가속을 포함하는 자동차 원격 분석 데이터

이러한 경우에 데이터의 시간적인 순서는 매우 중요하다고 할 수 있습니다. 이벤트를 발생 순서대로 표시하는 것이 시계열 데이터의 핵심적인 특징입니다.

https://docs.microsoft.com/ko-kr/azure/architecture/data-guide/scenarios/time-series