#title Data Modeling Case Study - 문제은행 [[TableOfContents]] 작성 중.. ==== 개요 ==== '문제은행'에 관한 주제로 데이터 모델링 연습을 많이 하고는 한다. 이 주제는 여러 고려사항들이 있지만, 이 글에서는 가산점과 합격여부 등의 상세한 조건들을 제외하고, 핵심적인 엔터티만을 살펴볼 것이다. 또한 랜덤추출에 관한 문제도 살펴볼 것이다. 여기서는 경찰공무원 1차 신체검사와 2차 필기시험으로 제한을 두도록 하자. 여기에서는 다중값 속성과 상태 조건에 따른 모델링 방법을 다룬다. (상태조건: 신체검사를 통과한 자만이 필기시험을 볼 수 있다.) ==== 데이터 모델 ==== 먼저 문제은행이 영속적으로 존재하는 데 필요한 엔터티들이 무엇일까? 일단 '문제'가 필요하다. 문제는 아래와 같이 선택형과 서답형이 있다. attachment:DataModelingCaseStudy-문제은행/01.png 서답형의 경우 채점이라는 행위를 하는 또 다른 엔터티도 필요하지만, 여기서는 경찰공무원 필기시험으로 제한했으므로 4지선다형 문제와 답만 있으면 된다. 4지선다형이므로 '문제'엔터티는 다음과 같이 엔터티를 표현할 수 있다. attachment:DataModelingCaseStudy-문제은행/02.png 여기서 고려해야 할 사항은 '보기' 속성이다.'보기'는 단일값이 아닌 다중값이다. 이 문제를 해결하기 위해 우리는 다음과 같이 모델링을 할 수 있다. 논리모델과 어커런스 다이어그램을 보면 논리모델이 실제로 어떻게 구현될 것인지 명확히 알 수 있을 것이다. (미리 언급하지만 여기에서는 다중값 문제를 풀어내는 정석인 유형 A를 선택할 것이다) attachment:DataModelingCaseStudy-문제은행/03.png 유형 A는 다중값 속성을 풀어내는 정석이다. '문제'와 '보기'는 식별자 관계를 가지게 된다. 식별자 관계는 '문제'의 식별자'문제번호'가 '보기'의 식별자의 일부가 됨을 말한다. 논리모델은 DA#의 기본적인 표기법인데, 실제로 구현된다면 우측의 어커런스 다이어그램처럼 '보기'의 식별자는'문제'의 식별자'문제번호' + '보기번호' 가 보기의 식별자가 된다. '정답'의 경우 실제 정답과 보기의 번호가 속성값이 될 수 있는데, 여기에서는 보기의 번호가 정답이다. 만약 '정답' 속성이 실제 정답을 값으로 가진다면 이도 다중값이 될 수 있다. 예를 들면, 원주율의 경우 3.14라고 쓸 수도 3.141592라고 쓸 수도 있다. 또한 '정답'속성이 '문제'에 있을 수도 있고, '보기'쪽에 '정답여부'와 같은 속성이 붙을 수 있다. 필자가 보기엔 식별자 관계라면 어느 쪽에 붙어있어도 괜찮지만, 비식별자 관계일 경우 '정답'이 '보기'쪽에 붙어 있다면 '문제'를 통해서만 정답을 알 수 있으므로 처리가 복잡해 질 수 있다.(이 문제는 잠시 후 더 자세히 살펴볼 것이다)관계형 이론의 '속성의 단일값' 때문에 좀 복잡해졌지만 문제, 보기, 정답을 하나의 단위로 생각하는 것이 중요하다. 유형 B는 '보기'를 하나의 단일값으로 표현한 것일 수도 있고, 괄호에 표시한 것처럼 다중값일 수도 있다. 처리되는 단위가 각각의 보기가 아닌 4개의 보기가 하나의 처리 단위가 된다면 단일값(원자값)이다.만약 문제번호가 1인 보기의 '(4) 4' 를 '(4) 8'과 같이 수정하려면 '(1) 1 (2) 2 (3) 3 (4) 4'를 '(1) 1 (2) 2 (3) 3 (4) 8'과 같이 상대적으로 큰 단위로 처리되어야 한다. SQL로 표현한다면 다음과 같다. {{{ update 보기 set 보기 = ' (1) 1 (2) 2 (3) 3 (4) 8 ' where 문제번호 = 1 }}} 유형 C는 유형 A와 비슷하지만 변화에 취약한 모델이다. 관계형 이론에서 속성의 값은 원자값이어야 한다는 원칙에 위배되는 모델이다. '보기'는 문제에 대해 선택할 수 있는 정답의 후보다. 하지만 보기1, 보기2, … 에서 '보기n' 에서 n은 순서를 의미하게 된다. '보기1'의 값과 '보기3'의 값을 서로 바꾼다면 '정답'까지 '1'로 변경해야 한다. 즉, '보기1'의 속성값은 '보기'의 의미와 '순서'의 2가지 의미를 가지게 되어 원자값이 아니게 된다. 이 모델은 경찰공무원 필기시험 문제가 4지선다형이 아닌 다른 유형으로 변경될 가능성이 거의 없다는 가정이라면 최적의 모델이다. 유형 D는 다중값을 XML으로 처리한 것이다. 변화에도 그리 취약하지 않으며, 모델자체도 간단하다. 하지만 XML 태그들이 용량이 큰 것과 XML처리 기술이 더 필요한 것이 문제다. 압축기법 등의 기술들이 추가적으로 필요할 수 있으며, 서버의 리소스를 더 많이 사용할 가능성이 높지만, 역시 처음에 언급했던 것처럼 모델의 유연성과 단순성은 전체적인 시스템 관점에서 볼 때에 매력적이다. 최종 모델에서는 위의 유형 A 모델에 '과목'엔터티를 추가할 것이다. '과목'은 '문제'의 종(kind)에 해당 된다. 이제 '문제'와 다른 엔터티 간의 관계(relationship)만 계속 찾으면 된다. 이제 문제를 제외한 나머지 엔터티를 살펴볼 차례다. 문제가 있으면 문제를 푸는 엔터티가 있을 것이다. 보통 '응시자'나 '수험생'정도로 생각할 것인데, 이는 관계의 명칭이지 엔터티의 명칭이라고는 볼 수 없다. 이 엔터티는'대한민국국민'이라 명칭을 짓도록 하자. 아마도 성명, 주민등록번호 등과 같은 기본적인 속성이 필요할 것이다. 여기서는 이 2개의 속성만 표현하도록 하자. 또한 시험의 일정도 필요할 것이다. 일정에 따라서 신체검사와 필기시험이 치러질 것이다. 결국 '시험일정', '문제', '대한민국국민'이 상호작용의 결과가 이 시스템의 정보의 핵심이 될 것이다. 먼저 '시험일정'과 '대한민국국민'의 1차 상호작용으로 신체검사가 이루어진다. 신체검사는 신체지수(키, 몸무게, 시력 등)와 같인 자격제한을 하는 속성으로 구성될 것이다.필기시험은 1차로 신체검사를 통과해야만 치를 수 있다. 필시기험의 문제는 한번 치러질 때마다 난이도를 적절히 맞추어 랜덤추출 된다. 정리해보면 다음과 같은 모델이 될 것이다. (더 상세하게 속성을 살펴야 하지만 여기까지만 하자) attachment:DataModelingCaseStudy-문제은행/04.png ==== 랜덤추출 ==== 랜덤추출 방법은 여러 가지다. 일반으로 랜덤함수를 이용하는 방법과 파일의 랜덤액세스 방법을 이용하는 방법을 사용한다. 이는 추첨을 하거나 할 때 사용되는데, 문제은행의 경우는 단순 랜덤추출만으로는 부족하다. 왜냐하면 난이도 조정을 해야 하기 때문이다. 즉, 어려운 문제(정답자가 적은 문제)와 보통 문제, 쉬운 문제가 적절한 비율로 섞어야 한다. 여기서 여러분은 다음과 같은 질문을 할 것이다. (질문을 생각조차 못했다면 집중을 안했기 때문이다) * '어려운', '보통', '쉬운'이란? * 예상되는 전체 평균은 어떻게 구하는가? 예를 들어, 이번 회차의 필기시험 문제는 평균 80 ~ 85점 정도가 되도록 문제를 추출한다. ''''어려운', '보통', '쉬운'이란?''' 시험이 어렵다는 것은 문제를 쉽게 맞추지 못한다는 것이다. 하지만 어렵고 쉽다는 것은 개인의 차이가 있다. 그러므로 상대적으로 정답자가 많은지를 비교하여 {{{[어려운 / 보통 / 쉬운]}}}의 경계를 결정해야 한다. 예를 들어 다음과 같이 문제에 대한 정답자의 통계가 있다고 가정해 보자. 각 회차는 100문제, 문제당 1점, 100명이 응시한다는 가정이다. (응시자수가 각 회차별로 다른 경우는 비율도 해도 된다) ||회차||문제번호||정답자|| ||1||1||98|| ||2||1||90|| ||3||1||90|| ||1||2||65|| ||2||2||66|| ||3||2||67|| ||1||3||13|| ||2||3||44|| ||3||3||27|| * 전체문제: 평균 = 62.22, 표준편차 = 27.62 * 1번 문제: 평균 = 93, 표준편차 = 3.78 * 2번 문제: 평균 = 66, 표준편차 = 0.81 * 3번 문제: 평균 = 28, 표준편차 = 12.7 대략적으로는 정답자수만 봐도 통계만 봐도 파악할 수 있다. 1번 문제는 대략 쉽다고 판단할 수 있고, 3번 문제는 어려운 경향이 있다고 판단 할 수 있다. 하지만 역시 {{{[어려운 / 보통 / 쉬운]}}}의 경계가 모호하다. 경계의 모호함을 없애기 위해서는 약간의 통계적인 지식이 필요하다. 정규분호, 평균, 표준편차, 이상치의 개념이 필요하다. 이에 대해서는 [이상치 제거 방법] 문서를 참고하도록 하자. 결론적으로는 다음과 같이 경계를 정하면 된다. 여기에서는 '보통'의 수준을 (m - 1σ) ~ (m + 1σ)의 범위로 잡았다. * 어려운: 35 이하 * 보통: (62.22 - 27.62) ~ (62.22 + 27.62) = 34 ~ 90 * 쉬운: 91 이상 1번은 평균 = 93이므로 '쉬운' 문제다. 2번은 평균 = 66으로 범위 34 ~ 90에 해당되므로 '보통'이며, 3번은 평균 = 28이므로 35이하의 범위이므로 '어려운'에 해당된다. 여기서 논란의 대상이 될만한 것은 2회차의 3번 문제의 정답자수다. 다른 문제에 비해 편차도 큰 것을 알 수 있다. 2회차의 3번 문제의 정답자수가 44인 것은 2회차의 응시자의 수준이 다른 회차의 응시자들보다 높다는 것을 의미할 것인데, 이는 [기준치와 편차치]를 참고하여 판단한다. '''회차의 필기시험 문제는 평균 80점 정도가 되도록 문제를 추출''' 이 문제역시 통계적인 방법이 사용되어야 한다. 랜덤 추출된 문제에 대해서 구간 추정을 해야 한다. 그러기 위해서는 t-test라는 통계적인 방법을 사용해야 하는데, sql로 구현하기란 쉽지 않다. 그러므로 엑셀이나 R, SAS, SPSS와 같은 통계툴로 시뮬레이션을 하는 방법이 가장 쉽다. 여기에서는 오픈소스 통계툴인 R을 이용하는 방법을 살펴보겠다. 문제의 정답자에 대한 통계가 있다고 가정했을 경우다. 만약 랜덤으로 추출된 데이터가 95,92,94,95,96,96,94,95,93,95,96,94 라면 기대되는 모평균은 다음과 같이 구하면 된다. {{{ > x <- c(95,92,94,95,96,96,94,95,93,95,96,94) > t.test(x) One Sample t-test data: x t = 264.2069, df = 11, p-value < 2.2e-16 alternative hypothesis: true mean is not equal to 0 95 percent confidence interval: 93.79540 95.37126 sample estimates: mean of x 94.58333 > }}} 1회 시뮬레이션 결과 구간은 93.79540 ~ 95.37126로 추정되었다. 80 ~ 85정도가 나올 때까지 반복해서 시뮬레이션 하면 된다.