R의 주요 데이터포맷인 데이터프레임(data.frame)을 사용하다보면 필터링을 해야 되는 경우가 많다.

필터링하는게 어렵지는 않지만 데이터프레임의 데이터가 많을 수록 성능이슈가 발생한다.


데이터가 클수록 필터링 속도는 현저하기 느려진다.

물론 이러한 성능이슈를 도와주는 R의 여러 패키지가 존재하며 대표적으로 data.table 패키지가 있다.

해당 패키지는 RDBMS와 유사하게 데이터를 인덱싱하여 보다 빠른 필터링 및 그외 여러기능을 제공한다.

(더 자세히 알고 싶다면 여기를 참고)


여기서는 data.table과 같은 패키지를 사용하지 않고 보다 빠르게 필터링 하는 방법을 알아보고자 한다.

우선 기본적인 R에서 데이터프레임을 필터링하는 방법으로는 아래 2가지가 대표적(?)이다.


df1 <- data[data$col1=='filter조건',]
df2 <- subset(data, col1=='filter조건') 


위와 같은 방법 data 데이터 사이즈가 500만건 정도의 데이터를 필터링 했을 경우 하드웨어 사양에 따라 조금씩 차이는 있겠지만 대략 두 방법 모두 10초 가량 소요가 되었다.


꽤 오랜시간이 소요되어 고민하던 차에 R에서 grep을 지원하는게 생각나서 아래와 같이 테스트를 해보았다.


df3 <- data[grep('filter조건', data$col1, ignore.case=T),]


테스트 결과 위 두가지 필터보다 50%이상 성능이 빠르게 나왔다.


내부적으로 어떤 차이가 있는지 좀 더 알아보고 싶지만 머 grep이니까 빠르겠지... 하고 쓴다...@.@;


끝.

저작자 표시
신고

'R' 카테고리의 다른 글

R grep을 이용한 데이터프레임 필터링  (0) 2014.10.22
R을 이용한 이상치 분석  (1) 2014.10.01
R on Hadoop and Amazon EMR  (0) 2014.08.19
R Oracle Connection  (4) 2014.02.24

WRITTEN BY
빵군
Web Programmer HOONS닷넷(http://www.hoons.kr) 2011 ASP.NET 시삽 http://about.me/y2kpooh

받은 트랙백이 없고 , 댓글이 없습니다.
secret


이상치(Outlier)라고 하면 특정 데이터 변수의 분포에서 비정상적으로 벗어난 값을 뜻한다.

예를 들면 어떤 학생이 신체검사를 했다고 치자 A학생은 몸무게와 키가 각각 100kg, 150cm나왔다.

이 A학생은 일반적으로 생각했을때 키에 비해 몸무게가 많은 것으로 보인다. 

(여기서는 몸무게와 키는 상관관계가 높은 것으로 간주한다.)

그럼 이 A학생은 속한 학년 반 또는 또래 아이들에 비해 비정상(여기서 이상치)이 맞을까?


위 질문에 대한 이상치 분석을 한번 해보자.

이상치 분석을 위한 모델 데이터는  A학생이 포함된 반의 학생들의 몸무게, 키 데이터이며 변수는 몸무게, 키로만 한정지어 분석해보고자 한다. 모델에 대한 종속변수는 몸무게이며 설명변수는 키가 된다.


우선 데이터를 한번 만들어보자.(실제 데이터가 없으니 ^^;)



name,weight,tall

A,100,150

B,55,156

C,50,153

D,54,165

E,70,164

F,56,155

....생략....

Y,49,153

Z,43,151


 


 std.data <- read.csv("outlier.csv", header=T)
summary(std.data)
boxplot(std.data$weight)

우선샘플 데이터를 R에서 가져와 데이터 분포와 몸무게에 대한 boxplot을 확인해보자.


boxplot을 통해보면 상단 whisker의 몸무게 70kg이상은 이상치라고 판단하고 있으며 해당 데이터에서는 위 boxplot에서 100kg은 이상치라고 판단했다. boxplot에서의 이상치는 몸무게 데이터간의 이상치라고 볼 수 있다.

몸무게와 키와의 상관관계가 있다고 가정하며 키가 클수록 몸무게도 커지기 때문에 몸무게 변수하나로 이상치를 판단하기는 좀 무리가 있을 수 있겠다. (저 몸무게 100kg의 학생이 키가 2m일 수도 있지 않나?? ㅡㅡ;)


그렇다면 설명변수 키와 종속변수 몸무게를 이용하여 회귀분석 모델을 생성하여 이상치를 팓단해보자.

회귀분석 lm함수를 이용하여 lm(종속변수 ~ 설명변수, 모델데이터)로 회귀분석 모델을 생성한다.

library(car)
std.lm <- lm( weight ~ tall , data = std.data)
summary(std.lm)
outlierTest(std.lm)
std.data[1,]

생성한 회귀분석 모델에 의한 이상치 판단을 하기 위해 outlierTest 함수를 사용한다.

해당함수는 모델에 대한 이상치 데이터를 각종 수치와 함께 쉽게 나타내 준다. 단 해당 함수는 car 패키지를 설치 후 사용가능하다. 실행 결과를 보면 아래와 같다.


> outlierTest(std.lm)
  rstudent unadjusted p-value Bonferonni p
1  7.62756         9.6225e-08   2.5018e-06
> std.data[1,]
  name weight tall
1    A    100  150

std.data에서 1번째 데이터가 이상치라고 판단했으며 해당 데이터를 확인해보니 A학생으로 나오는 것을 확인할 수 있다.


전체 실행코드



지금까지 정말 정말 간단한 말도 안되는 데이터를 가지고 이상치 분석을 해보았다... ^^;

그렇다면 이상치 분석은 어떻게 활용할 수 있을까? 

데이터만 가지고 있다면 어디든 활용할 수 있을 것이다. 예를 들면 상품에 대한 가격 이상치라던지 서버 모니터 정보를 이용한 이상치 등등...


물론 실제 많은 설명변수들이 존재하는 데이터를 가지고 이상치 분석을 하기는 쉽지 않다.

데이터 전처리도 쉽지 않고 위에서 설명하지 않은 회귀분석 모델, outlierTest함수를 통해 나온 수치해석 등 통계적 지식이 꽤 많이 필요한 건 사실이지만... 재미로 내가 몸담고 있는 회사의 데이터를 가지고 테스트 해보는 것도 나쁘지 않겠다.

끝.

저작자 표시
신고

'R' 카테고리의 다른 글

R grep을 이용한 데이터프레임 필터링  (0) 2014.10.22
R을 이용한 이상치 분석  (1) 2014.10.01
R on Hadoop and Amazon EMR  (0) 2014.08.19
R Oracle Connection  (4) 2014.02.24

WRITTEN BY
빵군
Web Programmer HOONS닷넷(http://www.hoons.kr) 2011 ASP.NET 시삽 http://about.me/y2kpooh

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. 데이터가 10만개 정도 되는데
    lm이나 glm으로 모델 만들고 outliertest을 했는데
    값이 동일한데도 몇 몇개만 이상치로 나오는지 혹시 알 수 있을까요?
    예로 만약에 데이터의 범위가 0에서 5까지고
    행에 1부터 10행까지 4로 모두 동일한데 6번째 행만 이상치로 검출되는 상황인데 혹시 왜그런걸까요?
secret

특정 데이터 분포를 쉽게 확인할 수 있는 수치로는 최소값, 중간값, 평균값, 최대값 그리고 사분위수가 있다.

사분위수는 데이터의 균등한 분할로 25%, 50%, 75%, 100%를 나타내며 1사분위수(1Q)는 25% 값에 해당한다.


사분위수를 알아내면 BOXPLOT과 같은 Chart를 통해 데이터 분포를 시각화 할 수 있을 것이다.

아쉽게도 R과 같은 통계언어의 경우는 쉽게 사분위수를 구할 수 있으나 MySQL의 경우는 지원하는 내장함수가 존재하지

않는다. 그래서 SQL를 통해 사분위수를 구할 수 있는 방법을 알아 보고자 한다.


boxplot 해석을 돕기 위한 그림(출처)



 
SET GROUP_CONCAT_MAX_LEN = 10485760;
SELECT
MIN(COLUMN_NAME) AS 'MIN',
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(COLUMN_NAME ORDER BY COLUMN_NAME SEPARATOR ','),',', 5/100 * COUNT(*) + 1), ',', -1)  AS `5TH PER`,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(COLUMN_NAME ORDER BY COLUMN_NAME SEPARATOR ','),',', 25/100 * COUNT(*) + 1), ',', -1)  AS `1ST QU`,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(COLUMN_NAME ORDER BY COLUMN_NAME SEPARATOR ','),',', 50/100 * COUNT(*) + 1), ',', -1)  AS `MEDIAN`,
AVG(COLUMN_NAME) AS 'MEAN',
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(COLUMN_NAME ORDER BY COLUMN_NAME SEPARATOR ','),',', 75/100 * COUNT(*) + 1), ',', -1)  AS `3RD QU`,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(COLUMN_NAME ORDER BY COLUMN_NAME SEPARATOR ','),',', 95/100 * COUNT(*) + 1), ',', -1)  AS `95TH PER`,
MAX(COLUMN_NAME) AS 'MAX'
FROM TABLE_NAME

원리는 간단하다. GROUP_CONCAT 함수로 데이터를 합쳐서 분위수에 해당하는 수치를 구해 SUBSTRING_INDEX함수로 데이터를 추출하는 방법이다. 참 쉽죠? 

아! 그리고  합쳐지는 데이터가 많을 경우 꼭 SET GROUP_CONCAT_MAX_LEN = 10485760; 와 같은 설정이 필요함


끝.

참고 : http://web.performancerasta.com/metrics-tips-calculating-95th-99th-or-any-percentile-with-single-mysql-query/



저작자 표시
신고

'db' 카테고리의 다른 글

Hive GROUP_CONCAT  (0) 2015.04.09
MySQL(MariaDB) 사분위수 구하기  (0) 2014.09.02
Oracle FlashBack  (0) 2011.04.05
DB 파티셔닝  (0) 2011.03.07

WRITTEN BY
빵군
Web Programmer HOONS닷넷(http://www.hoons.kr) 2011 ASP.NET 시삽 http://about.me/y2kpooh

받은 트랙백이 없고 , 댓글이 없습니다.
secret