Thursday, June 2, 2016

Javascript. The core.

Javascript 에 관련된 글 중 가장 인상 깊었던 글을 번역해 보려 한다. 중요 용어(object 등)는 영어를 그대로 사용하려 한다 (비록 괄호 안에 해석을 넣긴하겠지만). 원문에서는 각 섹션마다 관련이 있는 글에 대한 링크가 있으나 이 번역에서는 그 링크를 담지 않는다. 

변역해보고 나서 알게 된 사실이 이미 다른 분이 각 섹션에 링크되어 있는 detail을 번역해 놓으셨다. http://huns.me/?s=ECMA&x=0&y=0 에 보면 각 섹션의 detail 내용을 볼수 있다.


이 글을 ECMA-262-3 in detail 시리즈에 대한 개요와 서머리에 관한 것이다. 각 섹션은 거기에 해당하는 챕터를 레퍼런스로 담고 있기 때문에 더욱 자세한 내용을 알고 싶다면 관련 글을 읽어보길 바란다.

이글의 대상 독자 : 숙련된 프로그래머

우리는 ECMAscript의 기초가 되는 object의 개념에서 부터 시작하도록 한다.

An object
ECMAscript 는 객체지향 언어로서 object(객체)를 다룬다. 물론 primitives (기본자료형) 도 있지만 필요에 따라 이 역시 object로 변환된다.
object란 property(속성)의 집합으로 하나의 prototype object(프로토타입 객체) 를 가지고 있다. phototype은 object 또는 null 값이다.

object의 기본적인 예를 봐보자. 객체가 가지고 있는 prototype은 [[Prototype]] 이라는 속성으로 가르키게 된다. 그러나 그림에서 우리는 __<내부속성>__ 처럼 괄호 대신 밑줄을 이용하도록 하겠다. 특히 prototype object를 가르키는 속성으로  __proto__를 사용하도록 하겠다. 

코드를 보면 

var foo = {
  x: 10,
  y: 20};

위 코드의 foo라는 object는 두개의 속성(property) 와 prototype을 가르키는 내부적인 속성인 __proto__ 속성을 가지고 있다.


무엇을 위해 이 prototype이 필요한 것일까? 이 질문에 답하기 위하여 prototype chain (프로토타입 체인) 이라는 개념에 대해 알아보자

A prototype chain
prototype obejct는 단순히 object 이고 그렇기 때문에 그것 또한 자신만의 property(속성)을 가지고 있다. 만약에 prototype의 __proto__ 속성이 null값이 아닌 또 다른 prototype을 가리키고 있다면 이것을 prototype chain(프로토타입 체인) 이라 한다.

prototype chain은 객체들의 유한한 사슬(chain)으로서 상속(inheritance)와 속성의 공유(shared property)를 구현하는데 사용되어 진다.

거의 모든 부분이 유사하고 약간의 부분만 다른 두 object(객체)  가 있다고 할때, 좋은 디자인은 이미 하나의 object에 구현되어 있는 코드를 재사용하는 것이다. class(클래스) 기반의 언어에서는 이와같은 코드의 재활용 문법을 class-based inheritance(클래스 기반 상속) 이라고 한다. 공통되는 기능을 class A에 구현하고 class B, C가 A로 부터 상속을 받아 약간의 추가적인 코드만 넣는 것이다.

ECMAScript 에서는 class의 개념이 없다. 그러나 코드 재사용 문법은 별반 다르지 않으며(심지어 어떤면에서는 더욱 유연하다) prototype chain 을 이용한다. 이와 같은 상속을 delegation based inheritance 또는 prototype-based inheritance 라 한다. 

class A, B, C에 비슷하게 ECMAscript는 a, b, c 라는 object를 생성한다. object a가 공통된 부분을 가지고 있고 b, c가 각각의 다른 부분(속성이나 메소드)를 갖는다.

var a = {
  x: 10,
  calculate: function (z) {
    return this.x + this.y + z;
  }};

var b = {
  y: 20,
  __proto__: a
};

var c = {
  y: 30,
  __proto__: a
};

// call the inherited method
b.calculate(30); // 60
c.calculate(40); // 80

쉽지 않는가? 우리는 object b, c가 a에 정의되어 있는 속성에 접근하여 이용하는 것을 볼 수 있다. 

규칙은 단순하다. 만약에 특정 속성이 object 그 자신에게서 발견이 되지 않는다면 prototype chain에 있는 object의 속성을 확인하는 것이다. 만약 prototype의 속성에도 특정 속성이 없다면 prototype의 prototype의 속성을 확인하는 식으로 계속 속성을 찾게 된다. prototype chain에서 처음으로 찾아지는 속성을 사용하게 된다. prototype chain 에 의해 연결된 어떠한 prototype에도 찾고자 하는 특정 속성이 없으면 undefined 값을 리턴하게 된다.

주목해야 할것은 this라는 키워드는 현재 사용되어 지는 object를 가르키게 된다. a object에 있는 this 는 b.calculate에서 b object가 현재 사용되어 지는 object이기 때문에 b를 가르키게 된다. this.x 에서의 this 는 b에서 x라는 property 찾을 수 없기 때문에 prototype chain에 의해 a에 있는 x 속성을 가르키게 된다.

만약에 object의 prototype이 명시 되어 지지 않았다면 __proto__라는 속성은 Object.prototype을 가르키게 되며 Object.prototype의 __proto__ 속성은 null 값을 가르키게 되고 이는 prototype chain의 마지막 링크가 된다.

아래 그림은 위 코드의 a, b, c object간의 상속 관계를 도식화 한 것이다.


ES5 에서는 아래와 같은 방법으로 prototype-based inheritance를 구현할 수 있다.

var b = Object.create(a, {y: {value: 20}});
var c = Object.create(a, {y: {value: 30}});

때때로 갖은 property 구조를 갖지만 그 값은 다른  object를 만들 필요가 있을 때가 있다. 이와 같은 경우 constructor function (생성자 함수) 를 사용할 수 있다.

Constructor
object 생성에 있어 constructor 함수는 또다른 유용한 방법이다. constructor함수를 이용해서 object 를 생성하게 되면 새로이 생성되는 object의 prototype object를 자동으로 설정할 수 있게 된다. constructor 함수에 의해 생성되는 object가 가르키게 될 prototype object 는 constructor 함수의 prototype 속성에 연결되어 있다 (주의 할것은 여태 prototype이라는 용어를 object를 의미하는 것으로 사용하였는데 constructor 함수의 prototype 속성은 말그대로 속성의 key 값을 의미한다).

prototype chain 섹션의 코드를 아래와 같이 constructor 함수를 이용한 방법으로 변환할 수 있다(object a의 역할을 여기서는 Foo라는 constructor 함수가 대신하게 된다). 

// a constructor functionfunction Foo(y) {
  // 이것이 constructor 함수 이다.
  // constructor 함수를 이용해 object를 생성시 y 값을 받아서
  // 생성된 object 자신만의 y값을 갖게 된다.
  this.y = y;}
 
// also "Foo.prototype" stores reference
// to the prototype of newly created objects,
// so we may use it to define shared/inherited
// properties or methods, so the same as in
// previous example we have:

// inherited property "x"
Foo.prototype.x = 10;

// and inherited method "calculate"
Foo.prototype.calculate = function (z) {
  return this.x + this.y + z;};

// now create our "b" and "c"// objects using "pattern" Foovar b = new Foo(20);var c = new Foo(30);

// call the inherited method
b.calculate(30); // 60
c.calculate(40); // 80

// let's show that we reference// properties we expect

console.log(

  b.__proto__ === Foo.prototype, // true
  c.__proto__ === Foo.prototype, // true

  // also "Foo.prototype" automatically creates
  // a special property "constructor", which is a
  // reference to the constructor function itself;
  // instances "b" and "c" may found it via
  // delegation and use to check their constructor

  b.constructor === Foo, // true
  c.constructor === Foo, // true
  Foo.prototype.constructor === Foo, // true

  b.calculate === b.__proto__.calculate, // true
  b.__proto__.calculate === Foo.prototype.calculate // true

);

위 코드는 아래와 같이 도식화 될 수 있다.
위 그림에서와 같이 constructor 함수에 의해 생성된 모든 object는 prototype object를 갖게 된다. Foo constructor 함수도 함수 이기 때문에 javascript에서는 함수도 객체이기에 __proto__ 속성을 가지고 있고 이는 Function.prototype 이라는 object를 가르키게 된다. 이 Function.prototype의 __proto__ 속성은 Object.prototype을 가르키기 된다. Foo의 prototype 속성은 object를 생성하는 constructor로서의 역할을 수행할때 생성되는 object의 prototype이 될 object를 가르키는 속성에 지나지 않는다. 

ES6 에서는 class 의 개념을 표준화 하여 constructor 함수 위에 class 라는 syntactic sugar를 만들었다.

// ES6class Foo {
  constructor(name) {
    this._name = name;
  }

  getName() {
    return this._name;
  }}

class Bar extends Foo {
  getName() {
    return super.getName() + ' Doe';
  }}

var bar = new Bar('John');
console.log(bar.getName()); // John Doe

이제 object의 특성을 바탕으로 ECMAScript가 어떻게 runtime program execution을 구현하는지 알아보자. 이것은 execution context stack(실행 문맥 스택)으로 불리는데 이것의 모든 요소들이 (ECMAScript가 항상 그렇듯) 추상적으로 object로 표현되어 진다.

Execution context stack
ECMAScript 코드에는 3가지 종류의 코드가 있다: global code, function code, eval code. 모든 코드는 execution context(실행 문맥) 안에서 평가되어 진다. global context(문맥)는 오직 하나만 있고 그 안에 많은 함수의 instance와 eval execution context(실행 문맥)들이 있다. 매번 함수가 호출될 때마다 함수 실행 문맥으로 들어가게 되고 함수 코드 타입을 평가하게 된다. 마찬가지로 eval 함수가 호출되면 eval 실행 문맥으로 들어가고 그것의 코드가 평가된다.

함수가 호출 될때마다 문맥이 생성되기 때문에 하나의 함수는 수많은 문맥을 생성할 수 있으며 아래 코드와 같이 서로 다른 문맥이 서로 다른 context state(문맥 상태)를 가지고 만들어 진다.

function foo(bar) {}

// call the same function,// generate three different// contexts in each call, with// different context state (e.g. value// of the "bar" argument)

foo(10);
foo(20);
foo(30);

곧 함수가 또 다른 함수를 호출할때와 같이 실행 문맥은 또다른 실행 문맥을 활성화 시킬수 있다. 이러한 상황은 stack(스택)으로 구현되어 있으며 이를 execution context stack (실행 문맥 스택) 이라 한다.

문맥을 활성화 시킨 문맥을 caller (호출자) 라 한다.  반면에 활성화 된 문맥은 callee (피호출자) 라 한다. 피호출자가 또 다른 문맥을 활성화 시키게 된다면(다른 함수를 호출하게 된다면) 이는 또 호출자가 된다.  

호출자가 피호출자를 활성화 시키면(한 함수가 다른 함수를 호출하면) 호출자는 자신의 실행을 중단하고 제어권을 피호출자에게 넘겨준다. 이는 피호출자가 스택에 쌓이게 되는 것이고 이 피호출자의 실행문맥이 활성화 되는 것이다(active execution context). 피호출자의 문맥이 끝나게 되면 제어권이 다시 호출자에게 넘어가고 나머지 문맥이 평가된다(혹은 실행된다). 피호출자는 단순히 return을 하거나 exception과 함께 끝나면서 스택에서 빠지게 된다.

곧  모든 ECMAscript 의 program runtime (프로그램 런타임)은 아래 그림과 같이 실행문맥 스택 (execution context stack = EC stack)으로 표현되며 이 스택에서 젤 위에 있는 문맥이 활성화 상태의 문맥 (active context) 가 된다.


위 그림을 자세히 설명하면, 프로그램이 실행될때 global EC 에 들어가게 되고 이는 스택에 맨 처음 쌓이게 된다. 그러면 global code에 따라 일부 초기화 (필요한 객체와 함수들이 만들어 지는것)가 이루어 진다. global 문맥이 실행되는 동안 global code가 다른 함수를 호출(활성화)하면 그 함수의 EC로 들어가게 되고 이 EC는 스택에 쌓이게 된다. 또는 초기화된 이후 runtime system(런타임 시스템)은 특정 이벤트가 발생하기를 기다리고, 특정 이벤트가 발생하면 이벤트에 관련되 함수가 호출되고(활성화) 그 함수의 EC로 들어가게 되며 이 함수의 EC가 스택에 쌓이는 것이다.

아래는 프로그램이 시작될때 global EC가 스택에 쌓이고, global code에서 특정 함수를 호출하여 특정함수의 EC가 스택에 추가 된 이후, 함수가 실행 완료되어 스택에서 없어진 프로세스를 나타내는 그림이다.

이것이 바로 ECMAScript의 runtime system이 어떻게 코드의 실행을 다루는가를 보여준다.

위에서 말했듯 스택에 있는 모든 실행 문맥은 객체로 나타내어 진다. 이제 실행문맥을 나타내는 객체의 구조와 property(속성)에 대해 알아보자


Execution context
execution context(실행 문맥)은 객체로 표현되어 진다. 모든 실행문맥은 context's state (문맥 상태) 라 불리는 속성(property)를 갖고 있다. 이 속성들을 통해 연관된 코드의 실행을 추적하게 된다. 아래는 문맥의 구조를 나타낸 그림이다.


실행문맥은 위의 3개의 속성(variable object, this, scope chain) 외에도 구현에 따라 추가적인 속성을 갖을 수 있다.

이 속성들에 대해 자세하게 알아보자

Variable object
variable object (변수 객체)는 실행문맥과 관련이 있는 데이터를 담는 그릇이다. 이것은 문맥에 정의된 variable(변수), function(함수)를 저장하고 있는 객체이다.

주의해야 할것은 함수 표현식 (함수 선언 말고) 은 변수 객체에 포함되지 않는다.

변수 객체는 추상적인 개념이다. 다른 문맥에서는 다른 객체로 표현된다. 예를 들면 global 문맥에서는 global object(객체) 자신이 변수 객체이다(그렇기 때문에 global 변수를 global object를 통해서 직접적으로 접근이 가능한 것이다). 

아래 global 실행문맥을 살펴보자.

var foo = 10;

function bar() {} // function declaration, FD(function baz() {}); // function expression, FE

console.log(
  this.foo == foo, // true
  window.bar == bar // true);

console.log(baz); // ReferenceError, "baz" is not defined

위 코드의 global 문맥의 변수객체(variable object) 는 아래와 같은 속성을 갖는 객체이다.


그림에서와 같이 baz 함수 표현식은 변수 객체에 포함되지 않는다. 이 때문에 baz 함수 자신 이외의 곳에서 baz를 호출하면 ReferenceError가 발생하게 되는 것이다. 

주의해야 할 것은 다른 언어와는 달리 ECMAScript는 오직 function(함수) 만이 scope(스코프)를 만든다. 특정 함수 안에서 생긴 변수나 또 다른 함수는 그 함수 밖에서는 보이지 않는다. 

eval 역시 새로운 실행 문맥을 생성하나 자신만의 변수 객체를 생성하지 않고 global 변수 객체를 이용한다던지 혹은 eval을 호출한 호출자 함수의 변수 객체를 이용한다. 

그러면 함수에서는 변수 객체는 무엇인가. 함수 문맥에서는 변수 객체가 activation object (활성 객체)로 표현되어 진다.

Activation object
함수가 다른 함수 혹은 global 문맥에서 호출되어 활성화 되면 변수 객체 역할을 하는 특별한 객체인 activation object (활성 객체)가 만들어 진다. 일단 활성객체는 함수가 받는 parameter(인자)와 특별한 객체인 arguments (인자들을 array 형태로 갖는 객체) 를 속성으로 갖게 된다. 그 뒤 이 활성객체는 변수 객체로 사용되어 진다. 즉 활성 객체는 인자, aruments를 추가적으로 포함한 변수 객체인 것이다.

아래 예시 코드를 보자

function foo(x, y) {
  var z = 30;
  function bar() {} // FD
  (function baz() {}); // FE}
 
foo(10, 20);

위코드 함수의 활성 객체는 아래 그림과 같다.


다시 한번 알수 있드시 함수 표현식을 활성 객체에 포함되지 않는다.

다음 색션에서는 scope chain(스코프 체인)에 대해 알아본다. ECMAScript 에서는 함수안에 또 다른 함수인 inner function (내부함수)를 사용할때가 있으며 이 내부함수에서 parent function (부모함수)의 변수에 접근할 수 있다. variable object(변수 객체)를 문맥의 scope object(스코프 객체)라 명명하였듯이 앞서 알아본 prototype chain 또한 소위 scope chain 이라 불린다.

Scope chain
scope chain 은 문맥 안의 코드에 나타난 identifier(식별자)를 찾기 위한 객체들의 리스트이다. 

scope chain(스코프 체인) 에서의 규칙은 prototype chain 에서의 규칙과 비슷하다. 특정 변수가 자신의 scope (변수 객체 혹은 활성 객체)에서 발견이 되지 않으면 부모의 변수 객체에서 찾는 것이다. 

문맥에 따라 identifier(식별자)는 변수의 이름, 선언된 함수명, 인자 등이 될 수 있다. 문맥의 변수 객체에서 찾을 수 없는 식별자를 free varible이라고 하는데 이 free variable을 찾고자 할 때 scope chain이 사용되어진다.

일반적인 경우 scope chain은 부모의 변수 객체와 자신의 변수 객체(부모의 변수 객체보다 앞에 위치함)의 리스트를 의미한다. 그러나 문맥의 실행 동안 다른 객체(with-objects 또는 catch-clauses의 특별한 객체)가 scope chain에 추가될 수 있다.

식별자를 찾을 때(look up) prototype chain의 방식과 유사하게 활성 객체부터 시작하여(함수의 경우) 상위의 부모 함수 변수 객체를 찾아보게 된다. 

var x = 10;

(function foo() {
  var y = 20;
  (function bar() {
    var z = 30;
    // "x" and "y" are "free variables"
    // and are found in the next (after
    // bar's activation object) object
    // of the bar's scope chain
    console.log(x + y + z);
  })();})();

scope chain 의 연결이 활성 객체의 __parent__ 속성에 의해 연결되어 있다고 여긴다. 이 __parent__ 속성이 부모의 활성 객체 (혹은 변수 객체)를 가리키게 된다. 이와 같은 방법이 ES5 lexical environments 에 사용된 방식(outer link라 칭한다)이다. scope chain를 나타내는 다른 방식은 단순히 참조가 되는 활성 객체를 순서대로 array에 담는 것이다. __parent__ 속성을 이용한 구현 방식을 이용하여 우리는 위의 코드를 아래와 같이 표현할 수 있다(부모의 활성 객체는 함수의 [[Scope]] 속성을 저장되어 있다. 주의해야 할 것은 __parent__ 속성은 활성 객체의 속성이고, [[Scope]]는 함수 자체의 속성이다). 


코드의 실행 중에 with문과 catch 절 객체 (clause object)에 의해  scope chain의 객체가 추가 될수 있다. 그리고 추가되는 객체가 단순히 객체이기 때문에 prototype을 가지고 있을 수 있다(prototype chain). 이같은 사실로 인해 scope chain을 통한 identifier(식별자) 검색이 two-dimensional (이차원) 적으로 이뤄질 수도 있다. (1) 먼저 scope chain 에 의한 링크가 고려가 되어 자신의 활성 객체 부터 부모들의 활성 객체까지 하나씩 깊이가 깊어지면서 (2) 그 단계에 있는 활성 객체가 가지고 있는 prototype chain에 연결되어 있는 모든 객체를 검사하게 되는 것이다.  

Object.prototype.x = 10;

var w = 20;var y = 30;

// in SpiderMonkey global object// i.e. variable object of the global// context inherits from "Object.prototype",// so we may refer "not defined global// variable x", which is found in// the prototype chain

console.log(x); // 10

(function foo() {

  // "foo" local variables
  var w = 40;
  var x = 100;

  // "x" is found in the
  // "Object.prototype", because
  // {z: 50} inherits from it

  with ({z: 50}) {
    console.log(w, x, y , z); // 40, 10, 30, 50
  }

  // after "with" object is removed
  // from the scope chain, "x" is
  // again found in the AO of "foo" context;
  // variable "w" is also local
  console.log(x, w); // 100, 40

  // and that's how we may refer
  // shadowed global "w" variable in
  // the browser host environment
  console.log(window.w); // 20

})();

위 코드는 아래와 같은 구조를 갖게 된다(__parent__ 속성에 의한 링크를 검색하기 전에 __proto__에 의한 체인을 먼저 검색한다.).



주의해야 할 것은 모든 ECMAScript들의 구현에서 꼭 global 객체가 Object.prototype을 상속받는 것은 아니다라는 것이다(특정 구현에서는 위의 구조화가 틀릴 수도 있다).

 부모의 변수 객체가 존재할 때까지 inner function (내부 함수)에서 부모의 데이터를 찾는 것은 전혀 특별한 일이 아니다. 단순히 scope chain 을 통해서 필요한 변수가 있는지를 확인하는 것이다. 그러나 문맥이 끝나게 되면 문맥의 state (상태 값) 와 그 문맥 자체가 사라지게 된다. 어떤 경우에는 inner function (내부함수) 가 부모 함수의 return 값이 될수도 있다. 이렇게 리턴된 내부 함수가  free variable (자신의 활성 객체에서 찾을 수 없는 identifier)를 가지고 있을 때 활성화(호출)된다면 어떻게 될 것인가? 이와 같은 문제를 해결하기 위한 개념을 closure라 부르며 이것은 scope chain 개념과 연관되어 있다.


Closures
ECMAScript에서 함수는 first-class 객체이다. 이 말은 함수는 다른 함수의 인자로 전달되어 질 수 있다는 것이다(이런 경우 인자로 전달되어 지는 함수를 "functional arguments", 줄여서 "funargs"라고 한다). "funargs"를 인자로 받는 함수는 high-order function (고차원 함수) 또는 수학에 가까운 용어로 operator (연산자) 라고 한다. 또한 함수가 first-class 객체이기 때문에 다른 함수의 리턴 값으로 함수가 리턴 되어 질 수 있다. 다른 함수를 리턴하는 함수를 function valued 함수이라 한다.

"funargs" 와 "functional value"에 연관된 두가지 개념적인 문제가 있다. 이 두 문제는 "Funargs problem" 이라고 불리는 문제로 일반화 될 수 있다(A problem of a functional argument). 그리고 이 "Funargs problem"을 해결하기 위해 closure(클로져)라는 개념이 나타나게 된 것이다. 이 두 개념적 문제에 대해 자세하게 알아보자(이 두 문제가 이전 섹션에서 언급한 함수의 [[Scope]] 속성에 의해 해결된다는 것을 알게 될 것이다).

"Funargs problem" 의 첫번째 문제는 "upward funargs problem" 이다. 이것은 한 함수가 다른 함수에 의해 "up"(caller의 밖으로 리턴)되어 지고 이 리턴된 함수가 free variable 사용하고 있을 경우이다. 부모의 문맥이 종료되어 사라진 이후 리턴되어진 inner function (내부함수)에서 부모 함수의 변수에 접근하기 위해서 inner function이 만들어 질 때 inner function의 [[Scope]] 속성에 부모의 scope chain을 저장해 놓는다. 그리고 이후에 리턴된 inner function이 활성화(호출)되면 inner function의 문맥의 scope chain에는 활성 객체와 [[Scope]] 속성에 저장된 부모의 scope chain이 담기게 되는 것이다. 

Scope chain = Activation object + [[Scope]]

다시 한번 강조해야 할 부분은 리턴되는 inner function이 만들어 지는 순간 부모의 scope chain이 저장되어 지는 것이다. 그래야만 나중에 부모 함수(caller)가 사라지더라도 free varible를 찾을수 (look up 혹은 resolve) 있기 때문이다. 

function foo() {
  var x = 10;
  return function bar() {
    console.log(x);
  };}

// "foo" returns also a function// and this returned function uses// free variable "x"

var returnedFunction = foo();

// global variable "x"var x = 20;

// execution of the returned function
returnedFunction(); // 10, but not 20

이같은 스타일의 scope(스코프)를 static scope 혹은 lexical scope 라고 한다. 위 코드에서 리턴된 bar 함수의 [[Scope]] 속성을 통해서 x 변수가 찾아지는 것을 확인할 수 있다. lexical scope 외에도 dynamic scope 라는 것이 있는데 이 경우 리턴된 bar 함수에서 찾아진 x 변수의 값은 10이 아니라 20이 된다. 그러나 ECMAScript에서는 이 dynamic scope는 사용되지 않는다.

"funargs problem" 중 두번째 문제는 "downward funarg problem" 이다. 부모 문맥은 존재하지만 identifier(식별자)를 찾는데 애매한 경우를 이야기 한다. 다시 말해 문제는 어느 scope(스코프)에 있는 identifier를 사용해야하는 것인가 라는 것이다. 정적으로 함수가 생성될때 저장된 scope(정의 되었을 때의 부모의 scope chain)를 사용해야 하는 건지 실행될때 자신을 호출한 함수의 문맥에 있는 scope(caller 의 scope chain)를 사용해야 하는 건지의 문제인 것이다. 이같은 모호성을 해결하고 closure를 구성하기 위해 static scope(함수가 정의될때의 부모 scope를 사용하는 것)가 사용되는 것으로 정해졌다.

// global "x"var x = 10;

// global functionfunction foo() {
  console.log(x);}

(function (funArg) {

  // local "x"
  var x = 20;

  // there is no ambiguity,
  // because we use global "x",
  // which was statically saved in
  // [[Scope]] of the "foo" function,
  // but not the "x" of the caller's scope,
  // which activates the "funArg"

  funArg(); // 10, but not 20

})(foo); // pass "down" foo as a "funarg"

closure를 가지기 위한 필수 조건은 static scope 이다. 그러나 어떤 언어들은 dynamic 과 static scope를 섞어서 사용하기도 한다. ECMAScript는 static scope를 사용하기 때문에 closure가 있고 이는 함수의 [[Scope]] 속성을 이용해서 구현되었다. 아래는 closure의 정확한 정의이다.

closure 는 code block(ECMAScript 에서는 함수를 의미)과 statically(= lexically 정적으로, 그러니까 함수가 생성될 때) 저장된 부모의 scope의 조합이다. 이 조합에서 함수는 free variable을 탐색하게 된다.

모든 함수가 생성 당시 [[Scope]] 속성을 갖게 되므로 이론적으로 모든 함수는 closure이다.

주의해야 할 것은 많은 함수가 똑같은 부모 scope를 공유할 수 있다는 것이다. global 문맥에 정의되어 있는 수많은 함수가 있는 경우가 이러한 예이다. 이 경우 한 함수의 [[Scope]] 속성에 있는 변수를 변경하면 똑같은 부모 scope를 공유하고 있는 다른 함수에도 이것이 반영된다. 곧 부모 scope가 공유되어 있는 closure 사이에는 부모 scope에 있는 변수의 변경이 전체 closure에 반영된다.

function baz() {
  var x = 1;
  return {
    foo: function foo() { return ++x; },
    bar: function bar() { return --x; }
  };}

var closures = baz();

console.log(
  closures.foo(), // 2
  closures.bar()  // 1);

위 코드를 구조화 하면 아래 그림과 같다.


이같은 특징때문에 loop 안에서 여러 함수를 만들 경우 혼란이 생길수도 있다. loop 안에서 counter를 이용하여 함수를 생성할 경우 모든 함수의 counter가 같은 값을 가지게 되된다. 이는 loop 안에 생성된 함수들이 부모 scope의 공유 하는 closure 이기 때문이다. 

var data = [];

for (var k = 0; k < 3; k++) {
  data[k] = function () {
    console.log(k);
  };}

data[0](); // 3, but not 0
data[1](); // 3, but not 1
data[2](); // 3, but not 2

이 같은 문제를 해결하기 위한 여러 가지 방법이 있다. 아래 코드와 같이 생성하고자 하는 함수 위에 부모 함수를 만들어서 새로운 scope를 scope chain에 추가하는 것이다.

var data = [];

for (var k = 0; k < 3; k++) {
  data[k] = (function (x) {
    return function () {
      console.log(x);
    };
  })(k); // pass "k" value}

// now it is correct
data[0](); // 0
data[1](); // 1
data[2](); // 2

주의해야 할 점은 ES2015 (=ES6) 에서는 block-scope 가 추가되었다는 것이다. 이전의 ECMAScript에서는 함수 기반의 scope 였는데 ES2015에서 let 과 const 라는 키워드를 추가하여 block 기반의 scope를 도입하였다. 아래 코드가 그 예이다.

let data = [];

for (let k = 0; k < 3; k++) {
  data[k] = function () {
    console.log(k);
  };}

data[0](); // 0
data[1](); // 1
data[2](); // 2

이제 실행 문맥의 마지막 속성인 "this" 에 대해 알아보자.

This value
this 값은 실행문맥과 연관이 있는 객체이다. 그러므로 this 값은 context object라고 명한다. 

그 어떤 객체도 문맥의 this 값에 의해 사용되어 질수 있다. 중요한 점은 this 는 실행 문맥의 속성이지 변수 객체의 속성이 아니라는 것이다. 

이는 매우 중요한데 변수와 달리 this 값은 절대 identifier resolution process(식별자를 찾기 위한 sope chain을 뒤지는 과정) 를 거치지 않기 때문이다. 즉 this 값에 접근할때 scope chain 검색 없이 바로 실행 문맥에서 찾는다는 것이다. 이 this 값은 오직 문맥에 들어갈때 한번만 생성된다.

주의: ES2015 에서는 this가 lexical environment의 속성이 되었다. 다시 말해 변수객체의 속성이 되었다는 것이다. 이는 arrow function을 지원하기 위한 것으로 arrow function은 부모 문맥으로 부터 상속받은 lexical this를 가지게 된다(함수가 정의 될 당시의 부모 문맥의 this를 자신의 this로 갖게 된다).

global 문맥에서는 this 값은 global object 자신을 가리키고 위 변수 객체 섹션에서 언급했드시 global object는 곧 global variable object (변수 객체) 이기 때문에 this 값은 global 변수 객체가 된다.

var x = 10;

console.log(
  x, // 10
  this.x, // 10
  window.x // 10
);

함수 문맥에서는 this 값은 호출될 때마다 달라질수 있다. 

// the code of the "foo" function// never changes, but the "this" value// differs in every activation

function foo() {
  alert(this);}

// caller activates "foo" (callee) and// provides "this" for the callee

foo(); // global object
foo.prototype.constructor(); // foo.prototype

var bar = {
  baz: foo
};

bar.baz(); // bar

(bar.baz)(); // also bar(bar.baz = bar.baz)(); // but here is global object(bar.baz, bar.baz)(); // also global object(false || bar.baz)(); // also global object

var otherFoo = bar.baz;
otherFoo(); // again global object
이유를 좀더 자세하고 알고 싶으면 http://dmitrysoshnikov.com/ecmascript/chapter-3-this/ 를 읽어보길 권하다(번역: http://huns.me/development/258).
이 글에서는 this의 설명이 자세하지 않으니 위 링크를 꼭 읽어보길 바란다. 사실 hoisting 만큼 쉽게 이해가 되지 않는 부분이 this 이기 때문에 위 링크의 글을 읽어보면 조금 더 명확해 질 것이다 (쉽게 기억하는 방법은 this가 포함된 함수를 호출하는 대상 곧 "." 앞의 객체 혹은 함수(예를 들어 someVarible.callFunction() 에서 callFunction 메소드에 this가 들어 있다면 someVariable이 caller, 곧 호출하는 대상)가 있다면 this는 그 대상을 가리키게 되는 것이고 그게 없다면 그냥 global을 의미). 

이 글은 Evernote에서 작성되었습니다. Evernote는 하나의 업무 공간입니다. Evernote를 다운로드하세요.

Tuesday, March 3, 2015

Ancestry Composition in 23andMe

23andMe에서 Ancestry Composition 이라는 자신의 혈통적 뿌리가 어디서 유래 했는가를 알수 있는 서비스를 제공한다. 이는 아마도 23andMe 라는 회사가 기반을 둔 미국의 다인종 혼혈 상황에서 자신의 뿌리에 대한 사람들의 관심에 대한 결과가 아닐까 한다.

0. Contents

1. Basics

2. Wrinkle #1:  사람들은 일반적으로 여러 인종 혹은 혈통(popuy)을 조상으로 갖는다 (특히나 미국의 경우).

3. Wrinkle #2: 어떤 DNA 혹은 chromosome이 엄마, 아빠에서 온건지 알수 없다.

4. Ancestry Composition Overview

5. Prep 1: The DataSet

6. Prep 2: Population selection

7. Step 1: Phasing

8. Step 2: Window Classification

9. Step 3: Smoothing

10. Step 4: Calibration

11. Step 5: Aggregation & Reporting

12. Using Close Family Members

13. Testing & Validation

14. Ancestry Composition's Future




Basics

특정 DNA marker와 지리학적 위치의 연관성의 정보를 가지고 조상을 추측하는 것.
예를 들면 mtDNA haplogroup 의 한 종류인 haplogroup H 는 유럽에서 많이 발견되고 호주나 미국에서는 거의 발견되지 않는다. 만약 누군가 haplogroup H에 관련된 DNA marker를 가지고 있다면 그 사람은 유럽인일 확률이 크고 native american 확률은 작게 된다. 이렇듯 여러 DNA marker를 이용하여 혈통을 추측하는 것이다.

Wrinkle #1:  사람들은 일반적으로 여러 인종 혹은 혈통을 조상으로 갖는다

문제는 어떤이는 조상이 여러 위치에서 온 혹은 여러 인종이 뒤섞여 있을 수 있다는 것.
곧 admixture (the genetic mixing together of previously-separate populations), 원래는 유전적으로 분리가 되어 있던 집단이 서로 섞이는 것. 유럽인의 경우 조상이 유럽전역에 걸쳐 뒤섞인 경우.

Wrinkle #2: 어떤 DNA 혹은 chromosome이 엄마, 아빠에서 온건지 알수 없다.

chip 을 통한 genotyping은 unphased 상태, 곧 서로 다른 locus의 genotype이 같은 chromosome에서 왔지는 알 수 없는 상태이다. phased 상태의 SNP이 정보가 더 많다.

Ancestry Composition Overview

admixture and unknown phase 의 genotype 정보에서 혈통을 추측하기 위해

  1. 조상을 찾고자 하는  snp chip을 phasing을 통해 haplotype을 추정한다.
  2. 다음에 각 chromosome 을 특정 window 사이즈별로 겹치지 않게 구획을 나눠 reference genome과 비교하여 각 window 별 조상을 찾는다. 각 window는 여러 조상의 genotype이 섞인 것이 아니라 하나의 조상으로 부터 유래한다는 가정하이다. 
  3. "smoothing"을 통한 phasing correction
  4. 결과의 정확성을 위한 calibrate result 

Prep 1: The DataSet

10,000여 명의 데이터

  • 23andMe 자체 데이터 : 조부모, 부모의 출생지가 같은 회원의 정보
  • public resource : Human Genome Diversity Project, HapMap, 1000 Genomes project
reference 중 너무 genetic relationship이 가까운 개체는 제외. outlier로 판단되는 개체도 제외. 대략 10% 정보 제거됨


Prep 2: Population selection

위는 유럽인들의 유전정보만을 가지고 PCA를 그린 결과이다. 왼쪽 상단의 Finns 들의 경우 명확하게 isolation되는 것을 확인할 수 있다. 그러나 역사적으로 집단이 섞인 경우가 있어서 구분이 뚜렷하지 않은 경우도 있다. 이렇듯 구분이 되는 reference population 을 선택하였다.

Step1: Phasing

BEAGLE 프로그램을 customized 해서 Finch라는 프로그램을 개발, 이 프로그램을 통해서 haplotype phasing을 한다. BEAGLE은 phasing을 할 모든 데이터가 프로그램을 돌릴 때 갖춰져야 한다는 가정하에 있는데 추가적으로 고객이 발생하는 상황에서는 이 가정이 여의치 않기 때문에 이를 효과적으로 해결하기 위해 Finch를 개발.

Step2: Window Classification

phasing을 마친 후에 chromosome 을 100개의 marker 사이즈의 window로 쪼갠다. 보통 chromosome당 5,000 ~ 40,000 개의 marker가 존재. marker DNA 서열을 input으로 reference population에 의해 training 된 SVM 을 이용하여 각 window 의 ancestry를 결정한다.

Step3: Smoothing

SVM을 통해 ancestry 가 결정된 window의 정보는 first draft 이다. 여기에는 2가지의 에러가 포함 될 수 있다.

  1. window에 ancestry를 할당할 때 비슷한 정도의 ancestry candidate가 있을 경우 잘못된 ancestry를 할당하는 에러
  2. phasing step에서 haplotyping의 에러

위 두가지의 오류를 HMM (Hidden Markov Model)을 통해 수정하는 단계를 smoothing이라고 한다.

예를 들어 X, Y, Z 3개의 집단이 있고 SVM을 통해 아래와 같이 각 chromosome 의 window 별 ancestry 가 할당되었다고 할 때

parent1 의 뒤에서 두번째 window가  X라고 되어 있는데 이는 Z와 유사한 정도의 ancestry확률을 갖고 있어서 SVM이 잘못된 판단을 한 것일 수 있다. 또한 phasing step에서의 에러로 parent1 의 첫 4개 윈도우 X-X-Y-X는 parent 2의 Z-Z-Z-Z와 바뀌었을 확률이 높다.

이를 Hidden Markov Model 을 이용해서 correction을 하면 아래와 같게 된다.
위의 예는 smoothing을 설명하기 위해 단순화 한것이고 사실은 아래와 같이 각 window에 대해 모든 집단의 확률을 구한다.
위 그림은 African-American customer의 chromosome2에 대한 smoothing의 결과이다. 아래쪽 패널을 보게 되면 왼쪽부터 핑크-그린-핑크 (african-american-african)가 나오는 걸 확인할 수 있다. 

Step4: Calibration

위 단계에서 나온 그래프가 옳은지 우리는 어떻게 알 수 있을까? 시뮬레이션을 통해 systematic bias가 있는지 알아보고 이 systematic bias를 보정하는 recalibration단계를 추가한다.

Step5: Aggregation & Reporting

threshold를 정해서 그 threshold를 넘는 ancestry의 사이즈 에 비례해서 최종적으로 ancestry를 report하게 된다. 예를 들면 위 그림에서 오른쪽 부위의 green segment은 비록 이 region은 다른 집단에서 유래할 확률( orange segment :east asia)이 있더라도 70%의 threshold를 넘겼기 때문에 너비에 비례한 만큼 (전체 chromosome에서 0.26 % 차지) native americas 의 확률에 더하게 된다. 좌측 중간의 blue segment의 경우 threshold를 넘긴 특정 european population이 없기 때문에 report를 하지 않게 된다. 이런 경우에는 각 집단의 확률의 합이 threshold를 넘길 경우 이 집단들을 포괄하는 group 인 Broadly Northern European 으로 report를 하게 된다.

Using Close Family Members

부모나 자식의 데이터가 있다면 Finch를 통한 phasing 이 매우 정확해진다. 이는 higher-resolution result로 연결된다. 자식보다는 한명의 부모의 데이터가, 한명의 부모의 데이터보다는 두명의 부모 데이터가 있는 것이 조금 더 정확해진다.

13. Testing & Validation

14. Ancestry Composition's Future





* 위 내용은
https://www.23andme.com/ancestry_composition_guide/
http://blog.23andme.com/wp-content/uploads/2012/11/20121027_ancestry_painting_methods_poster.pdf
를 참조함

Wednesday, February 25, 2015

Predicting human height by Victorian and genomic methods

요즘 한창 관심 있는 분야가 breeding, 그 이유는 genomics를 이용한 prediction이기 때문이다.

인간이 본능적으로 관심이 가는 것은 아마도 생존과 번식일 것인데 NGS의 발전으로 한참 hot한 분야가 personalized genomics 혹은 cancer genomics가 아닐까 싶다. 이 분야는 생존과 관련된 분야로 개체의 건강과 수명에 관심에 의한 것이다. 나머지 하나가 번식. 좀 더 나은 자손 혹은 내 유전자의 생존의 확률을 높이고자 하는 본능. breeding이야 말로 genomics 가 번식에 공헌할 수 있는 분야라는 생각에 흥미를 가지고 있다.

사실 breeding은 가축 특히나 생장 기간이 긴 소의 경우 역사가 오래된 학문인데 그 연구의 근간은 통계혹은 수학적 모델링과 함께 한다. 특히나 EBV(estimated breeding value) 를 구하기 위한 BLUP (best linear unbiased model) 은 선형식에 대한 깊은 이해야 필요하다. 그렇기에 linear modeling 에 대한 공부를 진행하고 있는데 저 밑딴에 깊숙히 공부하다보며 어느덧 가던 길을 잃어버리고 내가 지금 무엇을 하고 있나하고 방황을 하게 된다. 그런의미에서 기술의 저 깊은 바닥에서 올라와 한참 적용이 되고 있는 상황이나 최신의 동향을 훑어보는 것이 좋은 방법이라 생각한다. 그런의미에서 오랜만에 논문을 리뷰해본다.

Tuesday, February 17, 2015

Quantitative Genetics of Human Traits

0. Contents

1. Quantitative Genetics of Human Traits

2. The First Human Genetic Markers : ABO Blood Groups

3. Human Genomic Variation

4. Population Subdivision, Haplotype inference and linkage disequilibrium

5. Human Population Genetics

7. DNA Fingerprinting

8. Parentage Analysis

10. DNA Substitution Models

14. Linkage Analysis




1. Quantitative Genetics of Human Traits

1.1 Anthropometric variation

프랑스의 Alphonse Bertilon 등이 1800년대 후반에 anthropometrics 라는 것을 개발한다. 이는 요즘 말하는 continuous trait 인 physical variation 을 측정하기 위한 방법들이다. Francis Galton 은 부모에서 자식에게로 형질 유전의 일반적인 규칙을 찾기위해 이러한 continuous trait 의 variation을 분석하였다. 이후 이같은 연구는 R.A. Fisher에 의해 멘델의 법칙이 재조명 되면서 continuous trait의 유전에 대한 패턴을 설명하기까지 별 관심을 받지 못한다.  Fisher의 이론은 20세기 초반에 human disease genetics, animal breeding 에 핵심적인 역할을 하는데,  집단 유전학에서 형질에 영향을 주는 유전자에 대한 이해 없이 친척간의 trait variation의 패턴을 예측하는 것을 "quantitative genetics" 라 한다.

  •  continuous trait : discrete trait와 달리 수치화되는 형질
  • complex genetic trait : 여러 유전자의 영향을 받는 형질
  • 일반적으로 continuous trait 와 같이 수치화되는 형질은 그 원인이 여러 유전자의 영향에 의한것으로 complex genetic trait임 

1.2 Fisher's model

\(L\)개의 유전자들에 영향을 받는 특정 continuous trait 가 m 값으로 측정되었을 때, 각 유전자가 D 혹은 d의 allele 을 갖고 D를 갖는다면 \(+a/2\), d 를 갖는다면 \(-a/2\) 만큼 m 값에 영향을 미친다면
\[m=\Sigma_{i=1}^L x_i + e\]
\[ x_i = \left\{\begin{array}{r1} +a & if \; DD \\ 0 & if \; Dd \\ -a & if \; dd \end{array} \right. \]
\(e\)는 평균 0 이고 \(\sigma_{\epsilon}^2\) 분산을 갖는 환경에 의한 random effect
집단에서 이 trait의 평균과 genetic 분산을 구하면
\[ \bar{m} = \Sigma_{i=1}^L (aP_i - aR_i) = a\Sigma_{i=1}^L (P_i - R_i) \]
\[\sigma_G^2 = a^2\Sigma_{i=1}^L[P_i(1 - P_i) + R_i(1 + _2P_i - R_i)]\]
\(P_i, Q_i, R_i\) 는 i번째 gene 의 \(DD, Dd, dd\)의 allele frequency
* 위 분산에 대한 유도식 필요
total population variance  는
\[ \sigma_T^2 = \sigma_G^2 + \sigma_{\epsilon}^2 \]
이고 heritability of trait는 \[ H =  \frac{\sigma_G^2}{\sigma_G^2 + \sigma_{\epsilon}^2}\]
 trait 에 관련된 locus가 많을 수록 형질의 측정치 m의 분포는 정규분포에 가까워 진다.

1.3 Trait correlations between relatives

친척간의 trait 의 측정치를 이용해서 heritability 를 예측할 수 있다.
변수 쌍 (\(x_i, y_i\)) 의 correlation coefficient는
\[ \rho = \frac{[\frac{1}{n}\Sigma_{i=1}^nx_iy_i - \bar x \bar y](\frac{n}{n-1})}{\sqrt{\sigma_x^2\sigma_y^2}} \]
\[\bar x = \frac{1}{n}\Sigma_{i=1}^nx_i, \quad \bar y = \frac{1}{n}\Sigma_{i=1}^ny_i\]
\[\sigma_x^2= \frac{1}{n-1}\Sigma_{i=1}^n(x_i - \bar x), \quad \sigma_y^2= \frac{1}{n-1}\Sigma_{i=1}^n(y_i - \bar y) \]
Fisher 에 의해 부모 자식간의 correlation coefficient는 \[\rho_{FS} = \frac{1}{2}(\frac{\sigma_G^2}{\sigma_G^2+\sigma_{\epsilon}^2}) = \frac{1}{2}H\]
이므로 부모와 자식의 correlation  coefficient 를 알고 있다면 heritability \((H)\)를 예측할 수 있다.

1.4 Galton's mistake: regression to the mean

Galton이  자식들의 평균키와 부모의 평균키를 비교하는 내용을 1886년에 Nature지에 출판했다. Galton은 이 분석에서 부모의 평균키가 집단의 평균보다 높은 경우 자식의 키는 부모의 평균키보다는 작은 키가 되고 반대로 부모의 평균키가 집단의 평균키보다 작은 경우 자식의 키가 부모의 평균키보다는 크게 나온다는 사실을 발견했다. Galton 은 이를 부모 윗대의 조상이 보다 평균에 가까운 키를 갖기 때문에 어떠한 유전적 효과에 의해 이러한 현상이 나타난다고 생각하였다. 그러나 이는 단순히 통계적인 현상일 뿐이다.
키가 정규분포라고 이고 유전이 안된다고 가정하면 먼저 부모키를 랜덤하게 뽑고 자식 키를 랜덤하게 뽑았을 때 부모가 극단적으로 작을때 자식의 키도 극단적으로 작을 확률이 적어진다.

2. The First Human Genetic Markers : ABO Blood Groups

2.1 Introduction

1900년에 Karl Landsteiner 가 ABO가 혈액 타입을 발견한다(이로 인해 1930년에 노벨상을 받음). 혈액 검사는 A 혹은 B 의 antigen을 넣음으로서 확인한다(B 형 antigen을 넣었을 때 혈액의 응고가 일어나면 혈액에는 B antigen이 없었던 것이므로 B형이 아니게 된다.  AB형의 경우 어떤 antigen을 넣더라도 응고는 일어나지 않는다).
O 형의 경우 O 형을 나타내는 allele \((i)\) 가 다른 혈액형의 allele \((I^A, I^B)\)에 대해 recessive해서 \(ii\) allele 쌍만이 O형을 나타내기 때문에 \(i\) allele frequency는 O형 phenotype frequency \((f_O)\) 로 부터 쉽게 추정이 가능하다(\(p_i = \sqrt{f_i} = \sqrt{f_{ii}}\)). 그러나 phenotype frequency \(f_A, _B\) 로 부터 population allele frequency \(p_A, p_B\) 를 추정하는 건 쉽지 않다.
\[f_A = f_{AA}+f_{Ai}=p_A^2 + 2p_Ap_i =p_A^2 + 2p_A(1- p_A-p_B )\]
1955년 C.A.B Smith 에 의해 이 문제에 대해  expectation-maximization (EM) algorithm 이라고 불리는 방법을 이용한 해결방법이 고안되었다.



type O type A type B type AB
Observed 4,578 4,219 890 313

위와 같이 1000명의 사람들에서 혈액형을 조사했을 때 위 데이터로부터 EM algorithm 을 이요하여 allele frequency \(p_A, p_B, p_i \) 를 구해보도록 한다.

    1. 임의로 구하고자 하는 genotype의 개체수 (\(\hat n_{AA}, \hat n_{Ai},\hat n_{BB},\hat n_{Bi},\))를 정한다.
\[\hat n_{AA} = 100 \\  \hat n_{BB} = 100 \\ \hat n_{Ai} = n_{A} - \hat n_{AA} = 4219 - 100 = 4119 \\ \hat n_{Bi} = n_{B} - \hat n_{BB} = 890 - 100 = 790 \]
    2. "maximization" step : maximum likelihood 방식으로 현재 genotype count를 가지고 allele frequency를 계산한다.
\[ \hat p_A = \frac{2\hat n_{AA}+\hat n_{Ai}+ n_{AB}}{2n} = \frac{2 x 100 + 4119+313}{2 x 1000 } = 0.2316 \\ \hat p_B = \frac{2\hat n_{BB}+\hat n_{Bi}+ n_{AB}}{2n} = \frac{2 x 100 + 790+313}{2 x1000} = 0.06515 \\ \hat p_i = \frac{2n_{ii} + \hat n_{Ai} + \hat n_{Bi} }{2n}= \frac{2x4578+4119+790}{2 x 1000} = 0.70325 \]
    3. "expectation" step : Hardy-Weinberg Equilibrium 가정하에 2번 단계에서 추정한 allele frequency 를 이용하여 각 유전형의 수를 예측한다.
\[ \hat n_{AA}= n_A(\frac{\hat p_A^2}{\hat p_A^2+2\hat p_A\hat p_i}) = 596.4962  \\ \hat n_{Ai}= n_A(\frac{2\hat p_A \hat p_i}{\hat p_A^2+2\hat p_A\hat p_i}) = 3622.504 \\ \hat n_{BB}= n_B(\frac{\hat p_B^2}{\hat p_B^2+2\hat p_B\hat p_i}) = 39.40033 \\ \hat n_{Bi}= n_A(\frac{\hat p_B \hat p_i}{\hat p_B^2+2\hat p_B\hat p_i}) = 850.5997 \]
    4. 위의 "maximization" 과 "expectation" 단계를 반복한다.

2.2 Case-Control studies of disease-marker association

1925년 Bernstein 에 의해 tri-allelic locus for ABO blood types 가 발견된다. 1939년 Lionel Penrose 가 phenotypic trait간 linkage 를 검사하기 위해 association을 이용하는 일반적인 framework 를 만든다. 이후 1950년대에 초창기 case-control association study 중에 하나로 Aird 가 blood type과 5가지 암과의 association study를 진행한다 (아래 표, allele frequency 는 위 EM algorithm과 유사한 방법으로 구함).


2.2.1 Relative risk and odds ratio

Odds ratio (OR) 은 특정 인자가 기인하는 질병에 대한 위험도를 수치화하는 강력한 방법이다. 이는 Fisher (1935), Berkson (1953), Woolf (1955) 등 독립적으로 여러 연구에서 이용되었는데 Woolf 의 연구가 최초의 human genetic association analysis 의 적용이다.



case
(ex: cancer)
control
(ex: non-cancer)
exposed to the risk
(ex: smoker)
a b
unexposed to the risk
(ex: non-smoker)
c d

OR은 \[ OR =\frac{P_1}{1- P_1} / \frac{P_2}{1- P_2} = \frac{P_1(1-P_2)}{P_2(1-P_1)} \]
이고 종종 case와 control의 label 이 뒤바뀌더라도 값은 변하기 않고 기호만 바뀌기 때문에 Log Odds (LOD) 를 사용하기도 한다.
\[ LOD = log(\frac{P_1}{1-P_1}) - log(\frac{P_2}{1-P_2}) \]
\(P_1 = a/(a+c) =\) proportions of individuals exposed to the risk factor among cases
\(P_2 = b/(b+d) =\) proportions of individuals exposed to the risk factor among controls

*위 식에서 \(P_1, P_2\) 을 사용하면 보통 odds ratio 인 \(\frac{a}{b}/\frac{c}{d}\) 가 아니라 \(\frac{a}{c}/\frac{b}{d}\) 의 식이 된다. 하지만 결론적인 OR의 식은 동일하게 된다. 그리고 본문에 OR은 population frequency of the risk factor 에 독립적임을 증명하는 부분이 있는데 다시 한번 확인할 필요가 있다.

relative risk (RR)은 \[RR = \frac{Pr(case|exposed)}{Pr(case|unexposed)}=\frac{z_1}{z_2}\] 이고
\[OR=RR\times (\frac{1-z_2}{1-z_1})\] 이므로 \(z_1,z_2\) 가 매우 작으면(disease(=case)가 rare 한 경우) OR과 RR값은 거의 유사하게 된다.
보통 RR을 위의 식으로 바로 구할수 없는 경우는
\[RR = \frac{Pr(exposed|case)}{Pr(unexposed|case)} \times \frac{Pr(unexposed)}{Pr(exposed)} = (\frac{p_c}{1-p_c}) \times (\frac{1-p}{p})\]
\(p_c = a/(a+c)\) = frequency of the risk factor among cases
\(p = (a+b)/(a+b+c+d)\) = ovarall population frequency of the risk factor

2.2.2 Odds ratio estimators

population 전체에 대한 OR값을 추정하기 위해 sampling을 통한 OR 계산값을 사용한다. 이 OR값, 그러니까 관측된 frequency의 차이가 유의한가를 판단하기 위해 OR의 신뢰구간을 구해서 구해진 OR의 신뢰구간이 null hypothesis (OR = 1) 을 포함하고 있는지를 확인한다. 추정된 OR의 신뢰구간이 1을 포함하고 있으면 null hypothesis를 reject하지 못하고 1을 포함하고 있지 않으면 null hypothesis를 reject한다.
위의 표는 Aird (1954) 의 혈액형과 peptic ulcer 간의 linkage를 연구한 자료인다. 위 샘플에서 \(\hat{OR}\) 을 구하면
\[\hat{OR} = \frac{911\times 4219 }{579 \times 4578} = 1.45\]
이고 Woolf (1955) 가 고안한 OR의 standard deviation (\(\hat{\sigma}\)) 을 이용하면
\[\hat{\sigma} = \sqrt{1/a + 1/b + 1/c + 1/d} = \sqrt{1/911 + 1/579 + 1/4578 + 1/4219} = 0.057 \]
이고 95% 신뢰구간을 구하게 되면
\[\hat{OR} \in (1.45 - 1.96 \times 0.057 \times 1.45, \quad 1.45 + 1.906 \times 0.057 \times 1.45) = (1.28, 1.61) \]
로 이 95%신뢰구가는 1을 포함하지 않으므로 null hypothesis (혈액형과 peptic ulcer은 관련이 없다) 를 reject하게 된다.

3. Human Genomic Variation

3.1 Single Nucleotide Polymorphisms (SNPs)

mutation rate of nuclear DNA 는 굉장히 낮다(\(10^{-9}\)). 그래서 SNP이 있는 위치, 곧 locus에는 보통 2개의 allele(a set of variants)을 갖는다. 이 allele중 집단에서 비중이 적은 allele(=variant)를 minor allele 이라고 한다. 특정 locus (= location in DNA sequence) 에서 minor allele의 frequency가 predefined frequency(보통 0.05)보다 높으면 이 위치는 polymorphic 하다고 한다.

3.2 Genotypes, Haplotypes, Diplotypes

abc

3.3 Summarizing human genomic variations

abc

3.3.1 Allele frequency

abc

3.3.2 Genotype frequencies and Hardy-Weinberg proportions

abc

3.4 Genotype frequencies with inbreeding

abc

3.5 Population subdivision and the Wahlund effect

abc

4. Population Subdivision, Haplotype inference and linkage disequilibrium

4.1 Subdivision between Han chinese and Europeans
4.2 Marker heterozygosity
4.3 Inference of haplotype phase
4.3.1 Genotypes to diplotypes: A one to many mapping
4.4 Linkage disequilibrium

5. Human Population Genetics

5.1 Allele frequency change in populations
5.1.1 Continuous approximation of allele frequency
5.1.2 Migration between populations
5.1.3 Mutation
5.1.4 Selection
5.1.5 Genetic drift

7. DNA Fingerprinting

7.1 Introduction
7.2 DNA forensics
7.2.1 Calculating DNA fingerprint match probabilities

8. Parentage Analysis

8.1 Calculation of the likelihood ratio
8.2 Parentage probabilities using multiple loci

10. DNA Substitution Models

종간에 DNA substitution modeling에 대해 소개하고 species divergence에 대해 추정하고 sequence data로 부터 phylogenetic tree를 추측해본다.

10.1 Alignment of homologous sequences

phylogenetic analysis의 기본 전제는 sequence가 공통 조상으로 부터 유래하였다는 것이다. sequence alignment의 목표는 공통의 조상으로 부터 유래한 두개 혹은 그 이상은 sequence들의 유사부위를 정렬하는 것이다. 이 장에서는 alignment가 되어 있다는 가정하에 phylogeny를 추정하는 것에 초점을 맞춘다
* alignment는 Biological Sequence Analysis 라는 책을 참조한다


10.2 Pairwise percentage of substitutions

두 종의 molecular divergence 의 단순한 지표가 될수 있는 것은 두 sequence를 alingment한 뒤의
\[d = \frac{x}{n}\]
\(x\)는 두 sequence에서 차이가 나는 nucleotide의 갯수
\(n\)은 전체 비교 site

두 종간의 sequence 의 fixed difference를 DNA substitution 이라고 한다. 이 DNA substitution이 일어나는 이유는 한 종에서 mutation이 일어나고 이 mutation이 genetic drift 혹은 natural selection 에 의해 그 종에 fixed 되었기 때문이다.

mutation의 fixation에 영향을 미치는 것이 genetic drift 만 있을 경우(곧 neutral selection) Motoo Kimura 는 rate of substitution이 site-specific mutation rate와 동일함을 보였다. 이에 대한 증명은 아래와 같다.
diploid 인 chromosome을 갖는 개체의 특정 위치에서의 mutation rate 를 \(\mu\) 라 하면 size가 N (즉 개체수가 N) 인 집단에서의 예상되는 mutation의 수는 \(2N\mu\) 이다. 그리고 Sewall Wright에 의해 \(i\)개의 copy를 갖는 allele이 N개체 수의 집단에서 fixation 될 확률은 \(i/2N\) 임이 보여졌다.
rate of substitution, 곧 mutation 이 일어나고 fixation이 되는 rate는 (예상되는 mutation 갯수) x (fixation될 확률) 이라고 할수 있다 (mutation 이 fixation될 확률은 mutation은 하나의 새로운 allele이 나타나는 것이므로 \(1/2N\)이 된다).
\[v = 2N\mu \times \frac{1}{2N} = \mu\]
많은 genomic 영역에서의 mutation은 neutral하지 않은 경우가 많다. 이 경우 substution rate는 mutation이 neutral effect일 경우 보다 높거나 (positive selection) 낮게 된다(negative selection).
예를 들면 third codon position의 경우 이 위치에서의 nucleotide의 변화가 Amino Acid의 선택에 영향을 주지 않기때문에 이 위치에서의 substitution rate가 다른 위치의 substitution rate보다 높다. 이는 곧 Amino Acid의 변화가 negative selection임을 알 수 있다.

10.3 Modeling DNA substitutions

한 곳에서 substitution이 일어나고 같은 곳에서 또 substitution 이 일어날 경우 원래의 nucleotide로 돌아갈수 있기 때문에 단순히 substitution의 percentage 는 실제 일어난 substitution을 저평가 하게 된다. 이와 같은 문제를 처리 하기 위한 model 중 가장 초창기 모델이 Jukes and Cantor (1969), JC69 이다. JC69 model은 mutation 의 발생이 poisson분포임을 가정한다.
\[Pr(M) = \frac{e^{-vt}(vt)^{M}}{M!} \]
\(M\) = substitution 갯수
\(t\) = time
\(v\) = the rate of substitution per unit of time
* Poisson distribution :: \(f(k;\lambda)=Pr(X=k)= \frac{\lambda^ke^{(-\lambda)}}{k!} \)

위 식을 이용해서 substitution 이 한번도 안 일어날 확률은
\[Pr(M=0) = e^{-vt}\] 이고 한번 이상 substitution이 일어날 확률은
\[Pr(M \geq1)= 1-Pr(M=0)=1-e^{-vt} \]
또한 JC69 model은 4종류의 nucleotide로의 변화할 확률이 동일하다고 가정한다. 곧 특정 위치에서 substitution이 한번 이상일어나서 원래 T였던 allele이 A로 변화할 확률은
\[p_{TA}(t)=(1-e^{-vt})\frac{1}{4}\]
이다. 위 식의 앞부분은 한번 이상의 substitution이 일어날 확률이고 뒤의 \(\frac{1}{4}\)은 T allele이 A allele로 바뀔 확률이다.
반면바뀌거나 혹은 유지가 되서  T allele이 T allele로 될 확률은 아래와 같다.
\[ p_{TT}(t)=e^{-vt} + (1-e^{-vt})\frac{1}{4}=\frac{1}{4} + \frac{3}{4}e^{vt} \]

10.4 Substitution proportions under JC69 model

특정 위치에서 한번 이상의 substitution이 일어나서 최종적으로 원래의 allele과 다른 allele 될 확률은 \[ p_{i\not=j}(t) = (1-e^{-vt})\frac{3}{4}\]
이는 sequence의 모든 위치가 독립적이다라고 가정할 때 sequence에서 substitution이 일어나는 위치의 비율이라고 할 수 있다. 마찬가지로 sequence의 변화가 없는 비율은
\[p_{i=j}(t) = e^{-vt} + (1-e^{-vt})\frac{1}{4} = \frac{1}{4}+\frac{3}{4}e^{-vt} \] 가 된다.
위의 식들은 원래의 allele이 같은 allele로 변화 하는 것도 subtitution이라고 여겼다. 그러나 nucleotide가 변화가 없는 것은 substitution 이라 하지 않는다. 그렇기 때문에 원래의 substitution rate \(v\) 대신 그 값의 \(3/4\) 를 substitution rate라고 해야 JC69 model의 standard formulas가 된다.
\[\upsilon= \frac{3}{4}v\]이므로 \[v = \frac{4}{3}\upsilon\]를 위 식에 대체 해야 한다.

10.5 JC69 distance and divergence model

 nucleotide substitution의 비율 식을 변환하면 \(t\), 곧 시간에 관한 식이 되고 이는 두 sequence를 분리시킨 총 시간이 되고 두 sequence의 substitution rate 가 같다는 가정(molecular clock hypothesis)하에 이 total 시간은 divergence time의 2배가 된다는 사실로 divergence time을 추정할 수 있다.
위 nucleotide substitution proportion 식에서 \(v\) 대신 \(\frac{4}{3}\upsilon\)으로 대체한 뒤 \(t\)로 방정식을 풀게 되면 추정되는 total 시간은 아래와 같다.
\[\hat{t}=-\frac{1}{\upsilon}(\frac{3}{4})log(1-4/3\hat{p})\]
\(\hat{p}\) = the observed proportion of substitutions between sequences

substitution rate\(\upsilon\)을 안다면 \(t\)를 구할수 있고 divergence time 은 \(t/2\)가 된다.

예를 들어 사람과 침팬치의 non-coding nuclear DNA sequence의 substitution 비율이 대략 0.01~0.015가 되고 mammalian nuclear gene의 mutation rate \(\upsilon = 10^{-9}\) per year 정도로 알려져 있다. mutation이 neutral하다고 가정한다면 mutation rate가 곧 substitution rate가 되기 때문에 사람과 침팬치의 divergence time은 아래와 같이 계산 가능하다(\(\hat{p}=0.01 로 여김\)).
\[\frac{1}{2}\hat{t}=-\frac{1}{2} \times  \frac{1}{10^{-9}}(\frac{3}{4})log(1-4/3\times  0.01) = 5033633= 5 \mbox{MYA}\]

10.6 Kimura 2 parameter model

 JC69 model은 transition(A<->G, T<->C)과 transversion 이 일어날 확률을 동일하게 보기 때문에 현실적이지 않다. 여러 종의 sequence를 비교한 결과 transtion이 transversion 보다 훨씬 자주 일어난다. Kimura (1980)는 이에 대해 transition과 transversion의 rate를 다르게 하는 좀 더 현실적인 model을 제안했는데 이를 Kimura 2 parameter model (K80) 이라 한다.
첫번째 parameter가 relative rate of transitions versus transversions 인
\[\kappa = \frac{\alpha}{\beta}\]
\(\alpha = \) rate of transitions
\(\beta = \) rate of transversions

두번째 parameter인 ovarall substitution rate 는 아래와 같다.
\[\upsilon = \alpha + 2\beta\]
*transversion이 transition에 비해 2배의 가지수가 더 많기 때문에 x2를 한것으로 보임

위 두 parameter 하에 probability of transition 는
\[p_1(t) = \frac{1}{4} +\frac{1}{4}e^{-4\upsilon\kappa / (\kappa+2)} - \frac{1}{2}e^{-2\upsilon t(\kappa+1)(\kappa+2)}\]
이고 probability of transversion은
\[p_2(t) = \frac{1}{4} - \frac{1}{4}e^{-4\upsilon\kappa / (\kappa+2)}\]
이다.
transition과 transversion의 expected proportion \(S,V\)가 \(S=p_1(t), V=2p_2(t)\) 이고
위 \(p_1(t), p_2(t)\)식을 \(S, V\) 치환한다음에 연립방정식을 풀게 되면
\[\hat{t}= -\frac{1}{\upsilon}(\frac{1}{2}log(1-2S-V) - \frac{1}{4}log(1-2V))\]
\[\hat{\kappa} = \frac{2llog(1-2S-V)}{log(1-2V)} -1\]
를 얻게 된다.

14. Linkage Analysis

linkage analysis의 기초 요소와 recombination rate, linkage mapping, disease gene mapping 을 추정하는 예를 살펴본다.

14.1 Probability model of recombination

 meiosis 단계에서 chromosome이 replicated 된 후에 chromosome segment를 교환해서 겹쳐진 상태인 chiasma를 형성하고 그 segment를 교환하는 recombination이 일어난다.
\(r_n\)을 n번 chiasma가 형성되었을 때 recombination이 일어날 확률이라고 하면 \(r_0 = 0\) 이고
\[r_n = \frac{1}{2}r_{n-1} + \frac{1}{2}(1-r_{n-1}) = \frac{1}{2}\]
중간식의 첫번째 부분 \(\frac{1}{2}r_{n-1}\) 은 n-1번째까지의 chiasma 형성 시 recombination 이 일어날 확률(\(r_{n-1}\))에 n번째 chiasma 형성에서는 recombination이 일어나지 않는(\(\frac{1}{2}\))  을 곱한 확률을 의미하고 두번 째 term인 \(\frac{1}{2}(1-r_{n-1})\) 은 n-1번째까지의 chiasma생성시 recombinationd 이 일어나지 않을 확률에 n번째 chiasma생성시에 recombination이 일어날 확률의 곱을 의미한다.

14.2 Haldane's map function


같은 chromosome  상에 있는 두 marker의 거리는 recombination 되는 생식세포의 비율(expected fraction of recombinant gametes, \(\theta\)) 로 측정될 수 있다.
\[\theta = \frac{1}{2}Pr(n>0)\]
\(Pr(n>0) =\) 1번 이상 chiasma를 생성할 확률 (위 식에서 \(\times 2\)를 하는 이유는 chiasma 형성된 뒤 교차가 일어날수도 있고 안일어날수도 있기 때문)로 Haldane 는 두 marker간의 거리는 멀고 chiasma의 생성이 일어날 경우가 매우 적기문에 이 확률은 Poisson 확률일 거라 가정
\[Pr(n) = \frac{e^{-cd}(cd)^n}{n!}\]
\(c = \) chiasma formation rate per Mb
\(d = \) marker A, B 의 Mb 거리
\[Pr(n > 0) = 1 - Pr(n=0) = 1 - e^{-cd} \]
이므로
\[\theta = \frac{1}{2}(1 - e^{-cd})\]
\(cd\) 가 작아질수록 \(\theta\)는 0 에 가까워 지고 이 상태의 두 marker를 complete linkage 라 한다. 반면 \( cd\)가 커질수록 \(\theta\)는 1/2에 가까워 지고 이 상태를 unlinked 라고 한다.

14.3 Inferring recombination rates

\(c\), chiasma formation rate 를 구하는 방법으로는 Taylor 급수를 이용한 linear appoximation이 있다.
Taylor 급수
\[e^{-x} = 1-x+\frac{x^2}{2} - \frac{x^3}{6} + \frac{x^4}{24} - ...\]
에서 \(x\)가 작으면 \(\frac{x^2}{2}\) 이 후의 식이 매우 작아지므로
\[e^{-x} \approx 1-x\]
가 된다. 고로
\[\theta =  \frac{1}{2}(1 - e^{-cd}) \approx \frac{1}{2}(1-[1-cd]) = \frac{1}{2}cd\]
위 식을 변형하여 chiasma formation rate 를 구하면
\[\hat{c}_1 = \frac{2\theta}{d}\]
가 된다. 이는 \(\theta \leq 0.01\) 일 경우에만 어느정도 추정값이 맞게 된다.
linear approximation말고 원래의 식으로 부터 바로 chiasma formation rate를 구하면
\[ \hat{c}_2 = \frac{1}{d}log(1-2\theta)\]
\(\theta\)의 단위를 cM (centi Morgan)을 많이 쓰는데 1 cM은 1% recombination per miosis를 의미한다. 그렇기 때문에 \(c\) 의 단위는 cM/Mb가 된다.

예를 들면,
500 그룹의 관계가 없는 부모와 자식한명 (unrelated family trio)의 genotyping을 하였을 때 두 marker의 총 500명의 자식의 genotyping이 되었기 때문에 가능한 haplotype은 1000개가 되고 이중에서 모계쪽에서 recombination 이 일어나서 자식에게서 발견된 recombinant haplotype (\(Y_m\)) 이 124,  부계쪽에서 recombination이 일어난 recombinant haplotype (\(Y_p\)) 이 86개 라고 하면
\[\theta = \frac{124 + 86}{1000} = 0.21 \]
두 마커의 거리가 1.6 Mb라고 할 때 linear approximation 에 의한 chiasma formation rate는
\[ \hat{c}_1 = \frac{2\times 0.21}{1.6} = 0.2625 = 26.25 \; \mbox{cM/Mb}\]
exact estimator는
\[  \hat{c}_2 = \frac{1}{1.6}log(1-2\times 0.21) = 0.340 = 34.0 \; \mbox{cM/Mb}\]
가 된다.

14.4 Linkage maps

abc

14.5 Linkage mapping of disease genes

linkage mapping  의 목적은 하나 이상의 genetic marker와 unobserved disease locus 간의 recombination proportion (\(\theta\)) 를 구하는 것이다.  Medelian genetic disorder는 하나 혹은 몇개의 genotype에 의해 일어나는 질병이기에 linkage mapping 이 효과적인다. disease locus 에 있는 genotype의 penetrance  는 \(Pr(disease|genotype)\) 으로 정의한다. \(D\)가 disease allele 이고 \(d\)가 normal alllele일때, 각 genotype의 penetrance는
\[f_1 = Pr(disease|DD) \\ f_2 = Pr(disease|Dd) \\ f_3 = Pr(disease|dd)\]
이고 simple recessive Mendelian disorder는 \(f_1 = 1, f_2 =0,f_3 =0\), 반면 simple dominant disorder \(f_1=f_2=1, f_3 =0\) 이다. \(f_3\)를 phenocopy rate라고 하는데 disease allele이 하나도 없을 경우의 발병률이다.

D allele 의 frequency 를 \(p_D\)라고 할때, HWE 가정하에
\[\begin{eqnarray}  f(DD) &=& p^2_D \\ f(Dd) &=& 2p_D(1-p_D) \\ f(Dd \times Dd) &=& f(Dd)^2 = (2p_D(1-p_D))^2 \\ f(Dd \times dd) &=& f(Dd) \times f(dd) = p_D(1-p_D) \times (1-p_D)^2 \end{eqnarray} \]
\(f(Dd \times Dd)\) 는 genotype \(Dd\) 를 갖는 두 사람이 mating을 할 확률이다. 매우 rare한 disease라면 \(p_D\) 값이 매우 작을 것이고 그러므로 \(f(Dd \times Dd)\), 곧 질병을 갖는 사람끼리 결혼할 확률과  \(f(DD)\), 곧 homozygous disease allele을 갖는 사람의 확률 또한 매우 작을 것이므로 고려 대상에서 제외해도 될 것이다. 곧 질병과 관련된 가계애서 한쪽 부모만이 heterozygous disease allele을 갖는 경우만 고려해본다.

unobserved disease locus (D:disease allele, d: normal allele) 와 genetic marker (A, a) 의 거리, 곧 recombination proportion 가 (\(\theta\)) 라고 하면 한쪽 부모의 genotype이 Aa/Dd, 다른 한쪽의 부모의 genotype이 aa/dd에서 생식세포의 종류와 확률은 아래의 표와 같다.

 그리고 질병을 가지고 있는 여러 가족의 자식의 genotype와 그 가계의 갯수가 아래 표와 같을 때
위와 같은 결과가 나올 확률은
\[\begin{eqnarray} Pr(\Upsilon_1, \Upsilon_2, \Upsilon_3, \Upsilon_4 \; | \; \theta) &=& \prod_{i=1}^4 Pr(\Upsilon_i \; | \; \theta) \\ &=& [\frac{1}{2}(1-\theta)]^{\Upsilon_1} \times [\frac{1}{2}\theta]^{\Upsilon_2} \times [\frac{1}{2}\theta]^{\Upsilon_3} \times [\frac{1}{2}(1-\theta)]^{\Upsilon_4} \\ &=& [\frac{1}{2}(1-\theta)]^{\Upsilon_1 + \Upsilon_4}\; [\frac{1}{2} \theta]^{\Upsilon_2 + \Upsilon_3} \end{eqnarray}\]
가 될 것이고 maximum likelihood 방식을 취하면 이 확률을 maximize하는 \(\theta\)가 disease locus 와 genetic marker의 거리가 될 것이다. \(\theta\) 값이 0이되면 두 loci는 complete linkage관계인 것이고 1\2가 되면 unlinked된 상태이다.
위 식의 log10을 위한 값을 lod score라고 한다.