더북(TheBook)

가족 구성원 생존 확률의 병합

all 데이터 프레임에 각 탑승객의 생존 확률 prob와 어느 가족에 속하는지를 의미하는 family_id가 준비되었으므로, 생존 확률 prob를 좀 더 다양하게 모을 준비가 되었다. 여기서는 다음과 같은 정보를 all 데이터 프레임에 추가할 것이다.

avg_prob : 가족 구성원의 평균 생존 확률

maybe_parent, maybe_child : 특정 탑승객이 부모인지 자녀인지 여부

parent_prob, child_prob : 부모의 평균 생존율과 자녀의 평균 생존율

가족의 평균 생존율 avg_prob는 ddply( )로 family_id가 같은 행들을 모아 prob의 평균을 avg_prob에 저장하는 것으로 간단하게 구할 수 있다.

all <- ddply(all,
             .(family_id),
             function(rows) {
               rows$avg_prob <- mean(rows$prob)
               return(rows)
             })

다음은 각 탑승객이 부모 또는 자녀 중 어느 쪽에 속하는지를 알아볼 차례다. 부모인지 자녀인지 여부는 maybe_parent, maybe_child에 저장될 것이며, 부모 자녀를 판단하는 기준으로는 나이age 속성을 사용한다.

all <- ddply(all, .(family_id), function(rows) {
  rows$maybe_parent <- FALSE
  rows$maybe_child <- FALSE
  if (NROW(rows) == 1 ||
      sum(rows$parch) == 0 ||
      NROW(rows) == sum(is.na(rows$age))) {
    return(rows)
  }
  max_age <- max(rows$age, na.rm=TRUE)
  min_age <- min(rows$age, na.rm=TRUE)
  return(adply(rows, 1, function(row) {
    if (!is.na(row$age) && !is.na(row$sex)) {
      row$maybe_parent <- (max_age - row$age) < 10
      row$maybe_child <- (row$age - min_age) < 10
    }
    return(row)
  }))
})

이 ddply( )는 .(family_id)마다 행을 묶고 있으므로 한 가족에 해당하는 행들이 인자로 주어진 함수에 rows로 넘겨진다. 함수 내 if 문에서는 세 가지 조건을 검사하여 이 중 하나라도 해당할 경우 maybe_parent와 maybe_child 컬럼을 FALSE로 지정한다. 다음은 이 세 가지 조건이다.

NROW(rows) == 1
가족 구성원의 수가 한 명이라면 부모도 자녀도 아니다.

sum(rows$parch) == 0 : parch는 부모 또는 자녀의 수를 뜻한다. 만약 이 값이 모든 행에서 0이라면 가족 구성원 중 어느 누구도 부모 또는 자녀가 없다는 의미므로 모든 구성원이 부모도 자녀도 아니다.

NROW(rows) == sum(is.na(rows$age)) : 만약 모든 행에 나이가 저장되어 있지 않다면 누가 부모고 자녀인지 알 수 없다. 따라서 어느 누구에게도 부모 또는 자녀인지 여부를 지정하지 않는다.

조건을 통과하면 나이를 사용해 부모, 자녀 여부를 판단한다. 가족 구성원의 최소와 최대 나이를 각각 min_age, max_age에 저장한 다음, 만약 탑승객의 나이가 max_age - 10 이상이면 해당 탑승객을 부모로 간주하여 maybe_parent에 TRUE를 저장한다. 마찬가지로 나이가 min_age + 10 이하면 자녀로 간주하여 maybe_child에 TRUE를 저장한다. 이런 방법을 사용한 부모, 자녀 여부 판단이 완벽하다고는 할 수 없지만 어느 정도 쓸 만한 결과는 얻을 수 있다.

부모 자녀 여부를 판단하고 나면 부모의 평균 생존 확률(avg_parent_prob)과 자녀의 평균 생존 확률(avg_child_prob)을 다음과 같이 구할 수 있다.

all <- ddply(all, .(family_id), function(rows) {
  rows$avg_parent_prob <- rows$avg_prob
  rows$avg_child_prob <- rows$avg_prob
  if (NROW(rows) == 1 || sum(rows$parch) == 0) {
    return(rows)
  }
  parent_prob <- subset(rows, maybe_parent == TRUE)[, "prob"]
  if (NROW(parent_prob) > 0) {
    rows$avg_parent_prob <- mean(parent_prob)
  }
  child_prob <- c(subset(rows, maybe_child == TRUE)[, "prob"])
  if (NROW(child_prob) > 0) {
    rows$avg_child_prob <- mean(child_prob)
  }
  return(rows)
})

이 코드에서 가장 눈여겨볼만한 점은 avg_parent_prob와 avg_child_prob의 기본값이 가족의 평균 생존 확률인 avg_prob라는 것이다. 만약 NA를 기본 값으로 부여한다면, 부모 또는 자녀가 없는 탑승객들의 avg_parent_prob, avg_child_prob에 NA가 저장되게 된다. 이 경우 NA를 저장한 행의 수가 많아 모델링 알고리즘 적용 시 힘들게 찾아낸 부모와 자녀의 평균 생존율 정보를 무시하게 되어버릴 수 있다. 따라서 적당한 적절한 기본값을 부여했다.

부모의 평균 생존 확률과 자녀의 평균 생존 확률은 가족 내에서 maybe_parent 또는 maybe_child가 TRUE인 행들을 subset( )으로 찾아 평균을 구해 avg_parent_prob, avg_child_prob에 저장하는 방식으로 구현했다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.