거래수 (Transaction)
* T
* 총 거래된 빈도수
지지도 (Support)
* P(A∩B) = N(A∩B) / N(T)
* 두개의 항목이 동시에 일어날 확률 (A와 B가 동시에 구매된 빈도수)
* A->B 의 지지도 = (A와 B를 동시에 포함하는 거래의 수) / 전체 거래수
신뢰도 (Confidence)
* P(B|A) = P(A∩B) / P(A)
* A를 포함하는 거래 중에서 B를 포함하는 빈도수
* A->B 의 신뢰도 = (A와 B를 동시에 포함하는 거래수의 백분율) / A를 포함하는 거래수의 백분율
향상도 (Lift)
* P(B|A) / P(B) = P(A∩B) / P(A)P(B)
* A를 구매한 경우 B를 포함하는 경우와 B가 임의로 구매되는 경우의 비율
* A->B의 향상도 = (A와 B를 동시에 포함하는 거래수의 백분율) / (A를 포함하는 거래수) * (B를 포함하는 거래수)
- A와 B가 서로 독립적인 관계 (예: 과자, 후추)
LIFT > 1
- A와 B가 서로 양의 관계 (예: 빵, 버터)
LIFT < 1
- A와 B가 서로 음의 관계 (예: 지사제, 변비약)
주의사항
1. 신뢰도 수치가 높아도 지지도가 낮으면 구매율 자체가 낮기 때문에 연관관계에 대한 신뢰가 부족
2. 지지도와 신뢰도의 수치가 높아도 향상도가 낮으면 연관관계에 대한 신뢰가 부족
3. 구매율이 낮은 상품을 기준으로 구매율이 높은 상품의 동시 구매율을 계산하는 건 무의미. (향상도 수치로 판별)
예제
아래와 같은 상품이 팔렸을 경우
거래수 (T) |
품목 |
1 |
우유, 빵, 버터 |
2 |
우유, 버터, 콜라 |
3 |
빵, 버터, 콜라 |
4 |
우유, 콜라, 라면 |
5 |
빵, 버터, 라면 |
각 항목의 수치는 다음과 같다.
품목(A->B) |
지지도(S) |
신뢰도(C) |
향상도(L) |
우유 -> 빵 |
0.20 |
0.33 |
0.56 |
우유 -> 버터 |
0.40 |
0.67 |
0.83 |
우유 -> 콜라 |
0.40 |
0.67 |
1.11 |
우유 -> 라면 |
0.20 |
0.33 |
0.83 |
빵 -> 우유 |
0.20 |
0.33 |
0.56 |
빵 -> 버터 |
0.60 |
1.00 |
1.25 |
빵 -> 콜라 |
0.20 |
0.33 |
0.56 |
빵 -> 라면 |
0.20 |
0.33 |
0.83 |
버터 -> 우유 |
0.40 |
0.50 |
0.83 |
버터 -> 빵 |
0.60 |
0.75 |
1.25 |
버터 -> 콜라 |
0.40 |
0.50 |
0.83 |
버터 -> 라면 |
0.20 |
0.25 |
0.63 |
콜라 -> 우유 |
0.40 |
0.67 |
1.11 |
콜라 -> 빵 |
0.20 |
0.33 |
0.56 |
콜라 -> 버터 |
0.40 |
0.67 |
0.83 |
콜라 -> 라면 |
0.20 |
0.33 |
0.83 |
라면 -> 우유 |
0.20 |
0.50 |
0.83 |
라면 -> 빵 |
0.20 |
0.50 |
0.83 |
라면 -> 버터 |
0.20 |
0.50 |
0.63 |
라면 -> 콜라 |
0.20 |
0.50 |
0.83 |
결과적으로 빵->버터 가 가장 흥미도가 높으며,
향상도는 1.25로 같지만 신뢰도가 낮은 버터->빵이 그 다음이고,
우유->콜라, 콜라->우유 가 그 다음으로 흥미도가 높다고 볼 수 있다.
'흥미롭다'고 분석결과를 내기 위해선 최소지지도 임계값과 최소신뢰도 임계값을 만족시키는 데이터만을 포함시키며, 각 임계치는 임의로 결정한다.
예를 들어 어떤 결과테이블을 바탕으로 최소지지도 임계값을 0.6, 최소신뢰도 임계값을 0.75로 잡으면,
위의 결과테이블에서 '흥미로운' 데이터는 빵->버터, 버터->빵 이 된다.
다음은 구매데이터를 배열형태로 받아서 흥미도 분석을 위한 각 수치를 계산해서 반환하는 PHP 클래스와 사용예제.
class InterestingnessMeasure
{
// 결과데이터
private $aResult = array();
// 흥미도 분석결과 가져오기
public function get($aOrderList)
{
// 지지도 (S)
$this->aResult['S'] = $this->getSupport($aOrderList);
// 신뢰도 (C)
$this->aResult['C'] = $this->getConfidence($aOrderList, $this->aResult['S']);
// 향상도 (L)
$this->aResult['L'] = $this->getLift($aOrderList, $this->aResult['S']);
return $this->aResult;
}
// 지지도
public function getSupport($aOrderList)
{
$aProduct = $this->getProduct($aOrderList);
$aResult = array();
$iTransaction = count($aOrderList);
foreach ($aProduct as $A) {
foreach ($aProduct as $B) {
if ($A == $B) continue;
// N(A∩B) / N(T)
$iN_AB = 0;
foreach ($aOrderList as $v) {
if (in_array($A,$v) && in_array($B,$v)) ++$iN_AB;
}
$aResult[$A.'->'.$B] = round(($iN_AB / $iTransaction), 2);
}
}
return $aResult;
}
// 신뢰도
public function getConfidence($aOrderList, $aSupport=null)
{
$aSupport = ($aSupport == null) ? $this->getSupport($aOrderList) : $aSupport;
$aResult = array();
$iTransaction = count($aOrderList);
foreach ($aSupport as $k=>$v) {
$sProduct = substr($k, 0, strpos($k, '->'));
$iP_A = 0;
foreach ($aOrderList as $order) {
if (in_array($sProduct, $order)) ++$iP_A;
}
// P(A∩B) / P(A)
$aResult[$k] = round(($v / ($iP_A/$iTransaction)), 2);
}
return $aResult;
}
// 향상도
public function getLift($aOrderList, $aSupport=null)
{
$aSupport = ($aSupport == null) ? $this->getSupport($aOrderList) : $aSupport;
$aResult = array();
$iTransaction = count($aOrderList);
foreach ($aSupport as $k=>$v) {
$iArrowPos = strpos($k, '->');
$A = substr($k, 0, $iArrowPos);
$B = substr($k, $iArrowPos+2);
$iN_A = 0;
$iN_B = 0;
foreach ($aOrderList as $order) {
if (in_array($A, $order)) ++$iN_A;
if (in_array($B, $order)) ++$iN_B;
}
$iP_A = $iN_A / $iTransaction;
$iP_B = $iN_B / $iTransaction;
// P(A∩B) / P(A) * P(B)
$aResult[$k] = round(($v / ($iP_A * $iP_B)), 2);
}
return $aResult;
}
// 주문 테이블로부터 중복제거된 상품 리스트 가져오기
public function getProduct($aOrderList)
{
$aResult = array();
foreach ($aOrderList as $v) {
foreach ($v as $v1) {
if (!in_array($v1, $aResult)) $aResult[] = $v1;
}
}
return $aResult;
}
}
// 구매 테이블
$aOrderList = array(
array('우유', '빵', '버터'),
array('우유', '버터', '콜라'),
array('빵', '버터', '콜라'),
array('우유', '콜라', '라면'),
array('빵', '버터', '라면')
);
// 흥미도 계산
$InterestingnessMeasure = new InterestingnessMeasure;
$aResult = $InterestingnessMeasure->get($aOrderList);
// 출력
printf("품목(A->B)\t\t지지도(S)\t신뢰도(C)\t향상도(L)\n");
foreach ($aResult['S'] as $k=>$v) {
printf("%s\t\t%.2f\t\t\t%.2f\t\t%.2f\n", $k, $v, $aResult['C'][$k], $aResult['L'][$k]);
}
참조
http://www.slideshare.net/pongsor/7viiopen-flamingo-v01
http://databaser.net/moniwiki/wiki.php/%EC%97%B0%EA%B4%80%EC%84%B1%EB%B6%84%EC%84%9D
'PHP' 카테고리의 다른 글
[PHP] gzdecode() 함수가 없다고 에러 나올 때 (1) | 2012.08.28 |
---|---|
[PHP] 이미지 바이너리 데이터 가져와서 <img> 태그를 이용해 출력하기. (0) | 2012.08.26 |
[PHP] date('W') 의 버그(?) (0) | 2012.05.11 |
[PHP] empty() 사용시 에러 can't use method return value in write context ... (0) | 2011.12.05 |
[PHP] xdebug 설치했는데 var_dump가 예쁘게 안나올 때 (0) | 2011.12.02 |