외국의 블로그에 소프트엔지니어 채용 과정 중에 코딩 인터뷰에서 있을 법한 문제로 회자되는 "FizzBuzz" 문제가 있습니다. 


"FizzBuzz"문제는 


 - 1에서 100까지의 숫자를 출력하되, 

 - 만약 해당 숫자가 '3'으로 나누어지면 숫자 대신에 "Fizz"를 출력하고, 

 - 만약 해당 숫자가 '5'로 나누어지면 숫자 대신에 "Buzz"를 출력하며, 

 - 만약 해당 숫자가 '3'과 '5'로 모두 나누어지면 숫자 대신에 "FizzBuzz"를 출력


하는 코드는 짜라는 것입니다.  


1~100까지 하기에는 좀 길기 때문에 1~30까지의 숫자에 대해서만 FizzBuzz 문제를 풀어보면 아래의 Original -> FizzBuzz 변환 전/후 비교를 보시면 이해가 쉬울 것입니다. 




위의 FizzBuzz 문제를 푸는 방법이 여러가지가 있을 수 있을 텐데요, (1) if, elif, else 조건문 사용, (2) if, elif, else 조건문을 포함한 사용자 정의 함수, (3) 문자열 합치기 (concatenating strings) 의 세 가지 방법을 소개합니다. 



  (1) if, elif, else 조건문을 사용한 FizzBuzz 코딩



# (1) if, elif, and else

for num in range(1, 31):

    if num % 3 == 0 and num % 5 == 0:

        print('FizzBuzz')

    elif num % 3 == 0:

        print('Fizz')

    elif num % 5 == 0:

        print('Buzz')

    else:

        print(num)

 




  (2) if, elif, else 조건문과 사용자 정의 함수(UDF)을 사용한 FizzBuzz 코딩


사용자 정의 함수에 대해서는 조만간 포스팅 하겠습니다. ^^



# (2) User Defined Function

def fizzbuzz(num):

    if num % 3 == 0 and num % 5 == 0:

        return 'FizzBuzz'

    elif num % 3 == 0:

        return 'Fizz'

    elif num % 5 == 0:

        return 'Buzz'

    else:

        return str(num)

    

print("\n".join(fizzbuzz(num) for num in range(1, 31)))

 




  (3) 문자열 합치기(Concatenating Strings)을 이용한 FizzBuzz 코딩



# (3) Concatenating Strings

for num in range(1, 31):

    string = ""

    if num % 3 == 0:

        string = string + "Fizz"

    if num % 5 == 0:

        string = string + "Buzz"

    if num % 5 != 0 and num % 3 != 0:

        string = string + str(num)

    print(string)

 



아래 링크는 취업 인터뷰에서 FizzBuzz 코딩 문제를 듣고서 Tensorflow로 딥러닝 모형을 적용했다가 취업 실패했다는 (가상의?) 블로그 내용입니다.  


Fizz Buzz in Tensorflow (http://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/)


간단한 '주먹지르기'로 해결할 수 있는 문제를 단지 '최신 유행'하는 기술이라는 이유만으로  '공중 3회전 날아 뒤돌아 발 차고 손목날로 목 가로지르기' 기술을 적용할 필요가 있는가에 대해서 우스개 (가상?) 사례로 소개한 포스팅인 듯 합니다.  Interviewer 의 반응을 유심히 보면서... 인터뷰 과정을 한번 보시는 것도 재미있을 것 같습니다. ㅋㅋ


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾸욱 눌러주세요. 



Posted by R Friend R_Friend

이번 포스팅은 페이스북의 R User Group 에서 아이디어 토론 주제로 올라왔길레 한번 짜봤던 코드입니다.  


'0'과 '1'로 가지는 긴 벡터를 처음부터 끝까지 쭉 훓어가면서 


(1) '0'이 연속으로 3번 나오면 그 구간을 기준으로 나누어서 

=> (2) 나누어진 구간을 새로운 벡터로 생성하기


입니다.  


'0'이 연속으로 나오는 회수는 분석가가 필요로 하는 회수로 지정할 수 있도록 매개변수(argument)로 지정해서 프로그래밍해보겠습니다. 


간단한 예제 벡터로서, '0'과 '1'을 원소로 해서 30개의 무작위수(이항분포 랜덤 샘플링, 0이 80%, 1이 20%)로 구성된 벡터를 생성해보겠습니다. 


 

> rm(list=ls()) # clean all


# Sample vector


> set.seed(123) # for reproducibility

> vec_raw <- sample(c(0, 1), size=30, replace=TRUE, prob=(c(0.8, 0.2)))

> vec_raw

 [1] 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0

> vec <- vec_raw # copy for manipulation




R 프로그래밍을 하고자 하는 일의 아웃풋 이미지는 아래와 같습니다. 

'0'이 연속 세번 나오는 구간을 노란색으로 표시를 했습니다. 이 구간을 기준으로 원래의 벡터를 구분해서 나눈 후에(split), 각각을 새로운 벡터로 생성하여 분리해주는 프로그램을 짜보는 것이 이번 포스팅의 주제입니다. 




아래에 wihle() 반복문, if(), else if() 조건문, assign() 함수를 사용해서 위의 요건을 수행하는 코드를 짜보았습니다. 원래 벡터에서 제일 왼쪽에 있는 숫자가 '0'이 아니면 하나씩 빼서 빈 벡터에 차곡차곡 쌓아 놓구요, '0'이 3개 나란히 있는 '000'이 나타나면 그동안 쌓아두었던 벡터를 잘라내서 다른 이름의 벡터로 저장하고, '000'도 그 다음 이름의 벡터로 저장하면서 원래 벡터에서 '000'을 빼놓도록 하는 반복문입니다. assign() 함수는 저장하는 객체의 이름에 paste()를 사용해서 변화하는 숫자를 붙여주고자 할 때 사용하는 함수인데요, 자세한 사용법 예제는 여기(=> http://rfriend.tistory.com/108)를 참고하세요. 


아래 프로그램에서 'bin_range'에 할당하는 숫자를 변경하면 원하는 회수만큼 '0'이 반복되었을 때 구간을 분할하도록 조정할 수 있습니다.  가령, '0'이 연속 10번 나오면 분할하고 싶으면 bin_range <- 10  이라고 할당해주면 됩니다. 



##---------------------------------------------

# Vector Bin Split by successive '0's criteria

##---------------------------------------------


# Setting

vec_tmp <- c() # null vector

bin_range <- 3 # successive '0's criterion

vec_idx <- 1

vec_num <- 1


# Zero_Bin_Splitter

while (length(vec) > 0){

  if (sum(vec[1:bin_range], na.rm=T) != 0){

    vec_tmp[vec_idx] <- vec[1]

    vec <- vec[-1]

    vec_idx <- vec_idx + 1

  } else if (is.null(vec_tmp)){

    assign(paste0("vec_split_", vec_num), vec[1:bin_range])

    vec <- vec[-(1:bin_range)]

    vec_idx <- 1 # initialization

    vec_num <- vec_num + 1

  } else {

    assign(paste0("vec_split_", vec_num), vec_tmp)

    vec_tmp <- c() # initialization

    vec_num <- vec_num + 1

    assign(paste0("vec_split_", vec_num), vec[1:bin_range])

    vec <- vec[-(1:bin_range)]

    vec_idx <- 1 # initialization

    vec_num <- vec_num + 1

  }

}


# delete temp vector and arguments

rm(vec, vec_tmp, bin_range, vec_idx, vec_num)




아래는 위의 프로그램을 실행시켰을 때의 결과입니다.  원래 의도했던대로 잘 수행되었네요. 




=====================================================

아래의 코드는 페이스북 R User Group의 회원이신 June Young Lee 님께서 data.table 라이브러리를 사용해서 짜신 것입니다.  코드도 간결할 뿐만 아니라, 위에 제가 짠 코드보다 월등히 빠른 실행속도를 자랑합니다. 대용량 데이터를 빠른 속도로 조작, 처리하려면 data.table 만한게 없지요. 아래 코드는 저도 공부하려고 옮겨 놓았습니다. 


똑같은 일을 하는데 있어서도 누가 어떤 로직으로 무슨 패키지, 함수를 사용해서 짜느냐에 따라서 복잡도나 연산속도가 이렇게 크게 차이가 날 수 있구나 하는 좋은 예제가 될 것 같습니다. 이런게 프로그래밍의 묘미이겠지요? ^^  June Young Lee 님께 엄지척! ^^b



# by June Young Lee


library(data.table)


# 0이 세번 연속으로 들어 있는 아무 벡터 생성

vec <- c(0,0,0,3,10,2,3,0,4,0,0,0,1,2,50,4,0,0,32,1,0,0,0,1,1)


# data.table함수인 rleid이용하여 연속인 행들을 구분하는 idx1 컬럼 생성 

dt <- data.table(v1=vec, idx1=rleid(vec))


# idx1 기준으로 갯수를 세어 N 컬럼 생성 

dt[,N:=.N, by=idx1]


# 0이 세번 연속으로 나온 그룹(=N컬럼변수가 3인)을 idx2로 체크하기 

dt[v1==0&N==3,idx2:=1L]


# split 하기 위한 기준 벡터를 마찬가지로 rleid함수를 이용하여 idx3로 생성


dt[,idx3:=rleid(idx2)]


# data.table의 split 함수 적용하고, lapply 적용하여 v1 칼럼만 추출 

res <- lapply(split(dt, by="idx3"), function(x) x[,v1])

res

# $`1`

# [1] 0 0 0

# $`2`

# [1] 3 10 2 3 0 4

# $`3`

# [1] 0 0 0

# $`4`

# [1] 1 2 50 4 0 0 32 1

# $`5`

# [1] 0 0 0

# $`6`

# [1] 1 1


# 0이 3번 연속으로 나온 놈들을 남겨두도록 작성한 이유는, 

# 위치확인 & 확인용입니다. 

# 필요없으면, 아래 정도의 코드를 추가하면 되겠네요.


res2 <- res[!unlist(lapply(res, function(x) length(x)==3&sum(x)==0))]

res2

# $`2`

# [1] 3 10 2 3 0 4

# $`4`

# [1] 1 2 50 4 0 0 32 1

# $`6`

# [1] 1 1

 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되셨다면 아래의 '공감~'를 눌러주세요. 



Posted by R Friend R_Friend

Jupyter Notebook 이나 ipython 을 사용하다보면 향후 버전이 올라갈 때 변경될 사항 등을 알려주는 경고 메시지(warning message)가 거슬릴 때가 있습니다. 


이럴 때는 warnings 라이브러리를 사용해서 


 - (1) 경고 메시지를 무시하고 숨기거나 (Ignore warning message)

    : warnings.filterwarnings(action='ignore')


 - (2) 숨기기했던 경고 메시지를 다시 보이게 (Reset to default)

    : warnings.filterwarnings(action='default')


하면 됩니다.  


별 내용은 없는 포스팅이긴 한데요, 이게 또 몰라서 경고 메시지가 주피터 노트북의 화면을 잔뜩, 계속 채우는 걸 보고 있노라면 은근히 신경이 쓰이기도 하거든요. ^^; 




아래에는 경고 메시지가 나타났을 때 

import warnings

warnings.filterwarnings(action='ignore') 

로 경고 메시지를 무시하고 숨기기를 했다가, 


warnings.filterwarnings(action='default')

를 사용해서 다시 경고 메시지가 나타나게 해본 예제입니다. 




많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. 



Posted by R Friend R_Friend