티스토리 뷰

문제의 핵심은 너무 지엽적인 문제에 몰두하게 되어

전체적인 스케쥴을 보지 못한다는 것이다.


예를 들어, 

렘펠-지브 복잡도를 계산하는 R function을 만들었다.


lempel.ziv=function(s, alphabet) {

  

  n=sum(!is.na(s))

  s=s[!is.na(s)]

  if (sum(s %in% alphabet)!= n) { stop("Alphabet error!") }

  

  voc=s[1]; cmpl=1

  r=1; i=1; 

  while (r+i<=n) {

    Q="";

    repeat {

      Q=paste(Q,s[r+i], sep="")

      if (Q %in% voc) {

        cmpl[r+i]=cmpl[r+i-1]; i=i+1; }

      if(!(Q %in% voc) | !(r+i<=n)) { break }

    } # repeat

    if (r+i > n) break;

    

    voc=c(voc, Q); cmpl[r+i]=cmpl[r+i-1]+1;

    r=r+i; i=1; 

  }

  

  cmpl=cmpl/(1:n/log(1:n,length(alphabet)))

  return(cmpl)}



하지만 이 function은 NA를 무시하도록 만들어졌다. 

따라서 NA를 포함된 문자열을 계산하면 길이가 다른 렘펠-지브 복잡도 벡터가 생성된다.


NA를 적절히 처리하도록 function를 수정하고 싶다.

문제는 이 function을 만든지 1달은 되었기 때문에 

function을 수정하기엔 힘이 좀 들거란 거다. 시간도!


이 때는 임시방편을 사용하는 수 밖에 없다.

Data를 적절히 손질해서 어떻게든 굴러만 가게 해야 한다!


하지만 나는 그러지 못했다.

장장 3시간 동안 NA를 제대로 처리하는 function lempel.ziv를 만들었다.


### lempel.ziv 

###    with handling NAs

### ver. 14-8-23 from snippets.R

### 


lempel.ziv=function(s, alphabet=unique(s)[!is.na(unique(s))], na.action=getOption("na.action"), na.new.element=NA) {

  

  

  if (any(is.na(alphabet))) {

    alphabet=na.omit(alphabet)

    warning("any NA's in alphabet will be ignored.") }

  

  

  if(!is.atomic(s)) stop("vector input needed!") 

  # integrity check

  

  if(identical(na.action, na.omit) & !is.na(na.new.element)) { warning("NA's omitted but new letter for NA!")}


  s=na.action(s)

  n=length(s)


  if (n==0) { return(c()) }  


  if(!is.na(na.new.element)) {

  s[is.na(s)]=na.new.element

  if (!(na.new.element %in% alphabet)) {alphabet=c(alphabet,na.new.element)}

  else {warning("NAs are converted to a letter which exists already in alphabet!")} 

  }

  

  if(length(alphabet) != length(unique(alphabet))) { warning("Alphabet lettters possibly duplicated")}


  if (sum(s[!is.na(s)] %in% alphabet)!= sum(!is.na(s))) { stop("Alphabet error!") }

  

  if (all(is.na(s))) { cmpl=rep(NA,n); names(cmpl)=s; return(cmpl)}

  r=min(which(!is.na(s))); i=1; 

  

  v.n=c(rep(0,r-1),1); voc=s[r]; cmpl=c(rep(0,r-1),1)

  

  while (r+i<=n) {

    Q="";

    repeat {

      if (!is.na(s[r+i])) {

        Q=paste(Q,s[r+i], sep="")

        if (Q %in% voc) {

          cmpl[r+i]=cmpl[r+i-1]; v.n[r+i]=v.n[r+i-1]+1; i=i+1;}

        if(!(Q %in% voc) | !(r+i<=n)) { break }

      } else { cmpl[r+i]=cmpl[r+i-1]; v.n[r+i]=v.n[r+i-1]; i=i+1; if (r+i > n) break; }            

    } # repeat

    if (r+i > n) break;

    

    voc=c(voc, Q); cmpl[r+i]=cmpl[r+i-1]+1; v.n[r+i]=v.n[r+i-1]+1;

    r=r+i; i=1; 

  }

  

  if (length(alphabet) > 1) {

    cmpl=cmpl/(v.n/log(v.n,length(alphabet))) }

  else { cmpl=cmpl*0 }

  cmpl[!is.finite(cmpl)]=NA # for NaN when first elements are NA's

  names(cmpl)=s

  return(cmpl)

}


처음 이 function을 만들 때 2시간 걸렸는데...

이것이야말로 trial-and-error 기법의 전형이다.

guard rail에 차를 부딪치며 운전하는 방법이라고 하던데...


이상향은 hammock-driven인데...


근데 나는 CS(Computer Science) 전공도 아닌데 

왜 coding(사회과학에서 말하는 coding 말고)이나 하고 있는 걸까?


이 function은 두 가지 결정을 사용자측에서 내려야 한다.

1. NA를 지울 것인가, 말 것인가? 

na.action=na.omit # 지우자

na.action=na.pass # 지우지 말자

2. NA를 또다른 letter로 생각할 것인가? 아니면 없는 문자로 생각할 것인가?

na.new.element=0 # NA를 0으로 바꾸자.

na.new.element=NA # NA는 건너뛰자.


그래서 결론은,

위의 함수를 만드느라 논문을 빨리 쓰지 못하게 됐다는 것.


그리고 의문,

나는 왜 CS 전공도 아닌데, 이 함수나 만들고 있는가?


그런데 CS 말이 나와서 말인데,

프로그램을 만들 때 주의할 점은 boundary condition을 잘 고려해야 한다는 것이다.


예를 들어, R에서,

nchar("R rocks!")는 8이다.

nchar("")는 0이다.

그렇다면 nchar(NA)는?



댓글