R 프로그래밍과 데이터-처리

안녕하세요. 요 몇 개월 간 제 블로그의 포스팅이 없었죠?.. 요즘 데이터와 씨름하느라고 정신이 없었습니다. 이제 프로그래밍을 넘어서 데이터에 대한 작업을 조금씩 배워나가고 있습니다만 SQL 모델링은 조금 해봤으면서도 가장 기초적인 데이터처리 등은 서툴러서 작업을 하는 내내 생각하는 것은 국가에서 제공하는 커리큘럼을 조금 익혀볼까 라는 생각도 많이 하곤 합니다.

저는 은근 My-way 스타일이면서도 가끔씩은 이게 맞나 싶은 고민이 생길 때는 가이드라인을 타곤 합니다. 하지만 그 가이드라인을 보면서도 “비효율적이다”, “내 스타일은 아니다.”, “더 깔끔하게”라는 욕심을 가지게 되는데, 그러다보면 한참을 고민하다가도 진도 진행이 되지 않아서 요즘에는 짜다가도 내 스타일 아닌 코드에 대해서는 일단 방치해두었다가 조금 시간적인 여유가 있을 때 다시금 그 스타일을 바꿔서 코딩하는 것이 저의 스타일이 되버렸습니다.

Data Processing

소프트웨어 개발자 입장에서 데이터 처리… 사실 DBA의 직책을 가지신 분들이 주로 하는 일이라고 보는 것이 맞겠지만 소프트웨어 개발자가 직접적으로 데이터에 관여하는 경우는 극히 드뭅니다. 사실상 소프트웨어 개발자는 사용자의 측면을 고려하고 얼마나 편하고 더 좋은 결과를 낼 수 있느냐에 달려 있다고 생각했습니다.

하지만 이번에 데이터 처리를 하면서 사실상 이렇게 마구잡이로 마이닝한 데이터를 처리하여 정보와 지식으로 만드는 데는 여러 알고리즘을 사용해야 합니다. 따라서 데이터 처리와 소프트웨어의 개발은 반드시 반대의 측면을 가지지 않는다는 것을 알게 되었습니다. 소프트웨어를 개발하는 데 있어서도 사용자가 어떠한 인풋을 주게 되면 그에 따른 결과를 출력하는 것처럼 데이터를 처리하는 데도 어떠한 모아진 데이터를 정보와 지식으로 축적하는 데 있어 여러 알고리즘을 구현하는 것 또한 같은 일입니다.

ETL

데이터 처리 작업을 하면서 듣게 된 단어 중 하나가 바로 ETL이라는 단어였습니다. 사실 데이터 엔지니어에 입문한 것은 처음이었고 그 중 제가 제가 처음 했던 작업이 ETL이라는 것도 작업을 하는 도중 알게 되었습니다. ETL은 Extract(추출), Transform(변환), Load(적재)의 약자로 사실상 그렇게 어려워 보이는 작업은 아니지만 은근히 손이 많이 가게 된 작업 중에 하나였습니다.

제가 간단히 하고 있는 일을 말씀드리자면, 저는 어떤 이미지를 가지고 해당 이미지가 어떤 이미지인지, 크기는 얼마나 되는지, 만약 크기가 다르다면 일정 크기에 맞춰 변환하는 작업을 거쳐야 하고, 그거에 맞춰서 다시 DB에 적재하여야 합니다. 조금 다른 원리를 알게 된 것은 이미지 처리를 위해서 Not only SQL을 사용할 것이라고 생각했지만 그냥 SQL만을 가지고도 작업 처리가 원활하게 돌아갈 수 있음을 알게 되었습니다.

하지만 이미지 데이터의 추출은 픽셀 데이터가 될 수도 있습니다. 이를테면 OpenCV와 같은 컴퓨터 비전 라이브러리를 사용해서 픽셀 데이터를 분석하고 이를 통하여 데이터를 추출하는 방법도 있겠지만 요즘에는 메타데이터 활성화가 잘 되어잇는지 이러한 메타데이터를 마이닝하는 것만으로도 충분한 정보를 긁어올 수가 있습니다. 하지만 그것 역시 제가 필요한 모든 정보를 가져올 수 있는 것은 아닙니다. 그래서 PixelData를 사용해 표준 이미지로 저장하여 이를 다시 OpenCV로 처리하여 DB에 적재하는 방법을 쓰는가 하는 방법도 고려해보고 있습니다.

정확하게는 작은 규모의 프로그램은 파일 단위로 라이브러리나 패키지를 이용해 그 데이터들을 간단히 긁어와서 표현할 수 있지만 데이터의 규모가 커지면 그것 또한 한계가 생깁니다. 또 프로그래밍 언어에서 지원하는 자료 구조는 한정되어 있고 빅 데이터를 처리하기 위해 Data Structure 또한 정해져 있습니다. 이에 최적화 된 언어는 Python과 R 언어였고 저는 이러한 데이터 처리를 위해 R 을 공부하여야 했습니다.

R

제가 처음 본 R 언어는 Python과 유사한 언어였습니다. 변수 타입을 지정하지 않고 자유롭게 사용할 수 있는 스크립트 형태의 언어, 하지만 저에게 익숙해지는 데 너무나 많은 시간을 들여야 했습니다. 단순히 자료형 타입을 지정하지 않는 문제 때문이 아니라 여러가지 문법이 존재하였고, 평상시 자주 사용했던 OOP와 같은 프로그래밍은 잘 찾아볼 수 없었으며 모듈화는 또 어떻게 해야하는지를 잘 몰라 어떤 방식으로 접근해야하는지도 익숙하지 않았습니다.

1
2
3
4
5
6
7
8
# A 열
a_col <- c()

# B 얄
b_col <- c()

# Excel Sheet 형태의 DataFrame
df <- data.frame(a_col, b_col)
1
2
3
4
5
6
7
8
9
10
11
12
#include <vector>
#include <iostream>

using std::vector;

int main(int argc, char **argv)
{
// 2 way vector,,
vector<vector<int>> vec;

return 0;
}

맨 위에 있는 것은 R 코드, 그 아래에 있는 것은 C++ 코드입니다. 같은 형태의 코드이지만 조금 다릅니다. R은 Python과 마찬가지로 Index를 이름으로 접근할 수 있습니다. 하지만 C++에서는 Index 번호로 접근할 수 있죠. Java도 마찬가지입니다. R 언어에서 c()는 벡터를 초기화 하는 함수이고 그 함수 안에 요소를 넣으면 데이터가 저장됩니다. 단, 이중 배열을 사용할 수 없으며 이를 사용하고 싶을 경우, list를 사용하여야 합니다. 자세한 Data Structure에 대한 내용은 다음 포스트에서 자세히 다룰 수 있도록 하겠습니다.

처음 코드를 보시는 분들은 R 언어의 문법이 익숙하지 않을 수 있습니다. 보통은 변수를 선언할 때 =을 쓰는 반면, (물론 사용할 수 없는 것은 아닙니다.) 저것이 무슨 자료형의 변수인지, 아니면 Data Structure인데, 무슨 자료 구조인지… 아마 예측하실 수 없을 것입니다. 당연합니다. C++, Java와 같은 프로그래밍 언어에서 볼 수 있는 자료구조와 비슷하지만 이름도 다르고 형태도 조금씩 다르기 때문이죠. 혼란을 겪을 수 있습니다. 하지만 사용하다보면 데이터 처리하는 데 이만큼 편한 언어도 솔직히 있나 싶네요..

R을 이용한 데이터 처리

R을 사용하면서 낯설면서도 효율적이라고 생각했던 것은 바로 반복문을 줄여가면서 데이터 처리를 한다는 점에 있었습니다. 저는 이미지 데이터를 RDS 파일로 변환한 후, Data frame에서 적재하여 데이터를 처리하였는데, 이미지 데이터는 상대적으로 크게 모아지면 그를 읽어내는 데 작업 시간이 많이 소요됩니다. 따라서 저는 텍스트 데이터로 메타 데이터를 모은 다음 R에서 데이터를 처리하는 방법을 사용하였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
# Data frame..
df <- readRDS(file = "fileName.rds")

# 찾고자 하는 이름.
tagName <- "ImageType"

# Data frame analytics..
for(i in 1:length(df)) {
if(pmatch(df[[i]]$name, tagName, nomatch = FALSE) == 0) {
print("is Valid Tag !")
break
}
}
No name value
1 ImageID A01234
2 ClassID 1.2.3.456789
3 ImageType JPEG
4 Rows 512
5 Columns 512
6 Color CMYK
7 Date 20180123
8 Time 112800.22

만약 제가 처리하려고 하는 데이터들을 RDS로 위와 같이 저장했다고 하고, 이러한 데이터 프레임이 100개가 있다고 가정해 보도록 하겠습니다. 해당 이미지 메타데이터에 제가 원하는 태그가 있는지를 확인하려면 위와 같이 한 번의 반복문 O(n)을 사용하여 처리해야 합니다. 보통이라면 그렇겠죠..

하지만 반복문을 사용하지 않고도 할 수 있는 방법이 있습니다.

1
2
3
4
5
6
7
8
9
# Data frame..
df <- readRDS(file = "fileName.rds")

# 찾고자 하는 이름.
tagName <- "ImageType"

# Data frame analytics...
if(is.boolean(which(ifelse(df$name %in% tagName, TRUE, FALSE)) == 0))
print("is valid Tag !")

일반 프로그래밍 언어 방법이 아닌 R 프로그래밍만의 방법을 사용하면 위와 같은 코드로도 쉽게 내가 원하는 데이터를 찾아낼 수 있습니다. 특히 이미지 메타데이터는 각 이미지마다 어떤 정보가 있는지가 각각 다릅니다. 물론 같은 경우가 있을 수 있습니다. EXIF 표준 포맷 형식에 의해 같을 수 있지만 추가적으로 내가 원하는 메타 데이터가 있을 경우 각 카메라 제조사마다 다르게 정보를 제공하기 때문에 이러한 방법도 나쁘지 않게 사용할 수 있죠.

중요한 것은 이러한 데이터들은 모두 Data frame 형태를 사용하고 있으며 비슷하게는 리스트나 벡터를 사용해서도 같은 방법을 이용해 자신이 원하는 데이터를 뽑아낼 수 있습니다.

그렇다면 이는 어떤 원리로 동작하는 것일까요? 그것은 바로 which함수에 있습니다. which 함수는 어떠한 자료구조에서 해당 요소의 인덱스를 반환하는 함수로, 만약 찾는 요소가 없다면 0을 반환해 버립니다.

하지만 R의 조건문인 if 조건문은 C++ 언어의 조건문과 차이가 있습니다. 이들은 반드시 Boolean 데이터로 TRUE, FALSE만을 인식하며 Logical한 데이터에 대해서는 오류를 발생시키기 때문에 이에 대한 처리를 해줘야 합니다. 이 처리는 is.boolean 함수를 사용해서 쉽게 처리할 수 있습니다.

R에서는 ifelse 함수가 있습니다. 이는 if, else의 조건문과 거의 방식이 동일합니다. 하지만 한 줄로 간략하게 표현할 수 있다는 장점을 가지고 있으며, 첫 번째 인자에 조건을 적고, 두번째에는 참일 때, 세번째는 거짓일 때의 값을 적으면 쉽게 적용할 수 있습니다.

1
2
> ifelse(NA == 0, TRUE, FALSE)
[1] FALSE

자 이제 이를 which 문에 적용하면 TRUE일 때 index 번호가 나오기 때문에, Logical한 데이터를 뽑아내므로 is.boolean 함수를 이용해서 Boolean 값으로 바꿔주면 쉽게 나타나지게 됩니다.

고급

그러면 이 방법을 응용하여 이제는 내가 원하는 위치에 있는 태그의 이름을 가지고 Value 값을 가져오는 방법에 대해 알아보도록 하겠습니다. 아주 조금만 응용하면 됩니다.

1
2
3
4
5
6
7
8
9
10
# Data frame..
df <- readRDS(file = "fileName.rds")

# 찾고자 하는 이름.
tagName <- "ImageType"

for(i in 1:length(df)) {
if(pmatch(df[[i]]$name, tagName, nomatch = FALSE) == 0)
print(df$value)
}

보통 이렇게 ImageType의 모든 데이터를 긁어와야 하지만, 간단히는..

1
2
3
4
5
6
7
# Data frame..
df <- readRDS(file = "fileName.rds")

# 찾고자 하는 이름.
tagName <- "ImageType"

print(df$value[which(ifelse(df$name %in% tagName, TRUE, FALSE))])

이렇게도 할 수 있습니다. 후자의 방법이 더 좋은 이유가 있다면 코드도 간결해지지만 이렇게 할 경우 자동으로 Vector 형식으로 반환되기 때문에 출력할 때도 용이하고 저장할 떄도 더 용이해질 수 있다는 점이 배로 좋죠.

마치며….

여기까지 간단하게 제가 R을 이용한 데이터 처리에 대해 이야기 해봤습니다. 아마 역대 포스트 중에 제일 지루할지도 모르겠습니다. 뭔가 실습적인 위주보다는 저의 경험담을 적는 것에 급급했습니다. 데이터 처리 분야는 저도 처음 접하는 분야이고 많이 아는 분야가 아닙니다. 이번에 새로이 접하게 되면서 이러한 재밌는 점이 있었고 이를 공유하고 이야기해보자 적어보게 되었습니다.

지금 목표는 데이터 처리를 통해서 소프트웨어 개발해보는 것이 목표입니다. 하지만 데이터 처리가 대충 어떻게 이뤄지는지 알았고 일반적인 소프트웨어 개발과 차이가 있다면 이러한 데이터 전처리 작업을 개발 이전에 해줘야 한다는 것입니다. 그리고 이들 데이터를 처리하는 데 자그만하치 1개월의 시간을 소요하였습니다. 앞으로 실시간으로 들어오는 수많은 빅 데이터를 분석해야하는 과제가 있을 수 있을텐데 그 때는 어떻게 해결해야 할지를 많이 고민하고 생각해봐야겠습니다.

짧지만 긴 글 읽어주셔서 감사합니다 ^^;

0%