일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 통계
- 김양재
- 빅데이타
- 우리들교회
- openCV
- R
- 빅 데이타
- 김양재 목사님
- 딥러닝
- Deep learning
- c++
- 데이터 과학
- 확률
- 빅데이터
- Statistics
- data science
- No SQL
- 몽고디비
- Big Data
- MongoDB
- nodeJS
- WebGL
- 김양재 목사
- 주일설교
- node.js
- 인공지능
- probability
- Artificial Intelligence
- 빅 데이터
- Machine Learning
- Today
- Total
Scientific Computing & Data Science
[MongoDB] Aggregation / The Basic 본문
by Geol Choi |
MongoDB는 복잡한 데이터 분석을 위해 다양한 맵리듀스(Map Reduce) 등 다양한 집합(Aggregation) 도구를 제공한다.
count
count 연산자는 컬렉션 내의 도큐먼트 개수를 파악할 수 있는 메써드이다.
다음과 같이 "people" 컬렉션에 3개의 도큐먼트를 추가한 후,
> db.people.insert({username: "user1"});
> db.people.insert({username: "user2"});
> db.people.insert({username: "user3"});
count() 메써드를 이용하여 개수를 구하면 다음과 같다:
> db.people.find().pretty()
{ "_id" : ObjectId("53003fc9f00107dcac226820"), "username" : "user1" }
{ "_id" : ObjectId("53003fcaf00107dcac226821"), "username" : "user2" }
{ "_id" : ObjectId("53003fcbf00107dcac226822"), "username" : "user3" }
> db.people.count()
3
distinct
distinct 연산자는 주어진 key 값에 대해 서로 "구별"되는 모든 값을 찾는다. 예를 들어 다음과 같은 도큐먼트가 주어졌다고 하자.
> db.people.insert({username: "user1", age: 10});
> db.people.insert({username: "user2", age: 24});
> db.people.insert({username: "user3", age: 30});
> db.people.insert({username: "user4", age: 32});
> db.people.insert({username: "user5", age: 24});
> db.people.insert({username: "user6", age: 10});
> db.people.find().pretty()
{
"_id" : ObjectId("5300c91533e71a1e198a9d7d"),
"username" : "user1",
"age" : 10
}
{
"_id" : ObjectId("5300c91733e71a1e198a9d7e"),
"username" : "user2",
"age" : 24
}
{
"_id" : ObjectId("5300c91833e71a1e198a9d7f"),
"username" : "user3",
"age" : 30
}
{
"_id" : ObjectId("5300c91a33e71a1e198a9d80"),
"username" : "user4",
"age" : 32
}
{
"_id" : ObjectId("5300c91c33e71a1e198a9d81"),
"username" : "user5",
"age" : 24
}
{
"_id" : ObjectId("5300c91f33e71a1e198a9d82"),
"username" : "user6",
"age" : 10
}
다음과 같이 "distinct" 값은 컬렉션의 이름을, "key" 값은 구별할 대상이 되는 키 이름을 입력한다.
> db.runCommand({distinct : "people", key : "age"})
{
"values" : [
10,
24,
30,
32
],
"stats" : {
"n" : 6,
"nscanned" : 6,
"nscannedObjects" : 6,
"timems" : 0,
"cursor" : "BasicCursor"
},
"ok" : 1
}
즉, "people"이라는 컬렉션의 "age" 키에 대한 값을 구별하여(중복을 제거하고) 표현한다. 각 username에 대한 "age"는 [10, 24, 30, 32, 24, 10]인데 이 중 중복이 되는 [10, 24]를 한 번씩 제거하면 구별되는 "age"의 집합은 [10, 24, 30, 32]가 된다.
group
"group"은 보다 복잡한 집합을 수행한다. 그룹화 할 key를 선택하면 컬렉션을 선택된 key 값에 대해 각각 그룹을 만든다. 각 그룹에 대해 그룹 멤버인 도큐먼트들을 모아 결과 도큐먼트를 생성할 수 있다.
용법은 다음과 같다.
db.collection.group({ key, reduce, initial, [keyf,] [cond,] finalize })
db.colletion.group()은 다음을 포함하는 하나의 도큐먼트를 허용한다.
필드 |
형태 |
설명 |
key |
document |
그룹화 할 필드(들) |
reduce |
function |
그룹 오퍼레이션을 수행하는 동안 도큐먼트들에 대해 수행할 집합 함수(aggregation function) |
initial |
document |
집합 결과 도큐먼트의 초기화 |
keyf |
function |
optional. key 필드에 대한 대체 필드. 그룹화 key로 사용할 "key object"를 생성하는 함수를 지정. |
cond |
document |
optional. 컬렉션 내의 어떤 도큐먼트를 처리할 지를 결정하는 선택 기준. cond 필드 생략 시 컬렉션 내 모든 도큐먼트를 처리. |
finalize |
function |
optional. db.collection.group()이 최종 값을 반환하기 전 결과 세트 내 각 아이템을 실행하는 함수. |
다음 예로 설명하도록 하겠다. "orders" 컬렉션의 형태는 다음과 같다(아래의 형태로 여러 개의 도큐먼트를 준비했다고 가정한다):
db.orders.insert({
_id: ObjectId("5085a95c8fada716c89d0021"),
ord_dt: ISODate("2012-07-01T04:00:00Z"),
ship_dt: ISODate("2012-07-02T04:00:00Z"),
item: { sku: "abc123",
price: 1.99,
uom: "pcs",
qty: 25 }
})
상기 컬렉션을 "ord_dt"(ordered date; 주문 일자)와 "ship_dt"(shipped date; 선적 일자)의 두 개의 key로 그룹화 하되, "ord_dt"가 "01/01/2012(2012년 1월 1일)" 보다 큰(보다 최근의) 날짜인 것만을 그룹화 하면 다음과 같이 array 형태로 결과가 출력된다 (앞서 설명하였듯이 사용자가 위의 컬렉션 형태로 도큐먼트를 준비했다고 가정한다).
[ { "ord_dt" : ISODate("2012-07-01T04:00:00Z"), "item.sku" : "abc123"},
{ "ord_dt" : ISODate("2012-07-01T04:00:00Z"), "item.sku" : "abc456"},
{ "ord_dt" : ISODate("2012-07-01T04:00:00Z"), "item.sku" : "bcd123"},
{ "ord_dt" : ISODate("2012-07-01T04:00:00Z"), "item.sku" : "efg456"},
{ "ord_dt" : ISODate("2012-06-01T04:00:00Z"), "item.sku" : "abc123"},
{ "ord_dt" : ISODate("2012-06-01T04:00:00Z"), "item.sku" : "efg456"},
{ "ord_dt" : ISODate("2012-06-01T04:00:00Z"), "item.sku" : "ijk123"},
{ "ord_dt" : ISODate("2012-05-01T04:00:00Z"), "item.sku" : "abc123"},
{ "ord_dt" : ISODate("2012-05-01T04:00:00Z"), "item.sku" : "abc456"},
{ "ord_dt" : ISODate("2012-06-08T04:00:00Z"), "item.sku" : "abc123"},
{ "ord_dt" : ISODate("2012-06-08T04:00:00Z"), "item.sku" : "abc456"} ]
이번에는 "reduce" 필드를 이용하여, 상기와 동일한 오퍼레이션을 수행하되, 각 그룹에 대해 합을 계산해 본다:
db.orders.group( {
key: { ord_dt: 1, 'item.sku': 1 },
cond: { ord_dt: { $gt: new Date( '01/01/2012' ) } },
reduce: function ( curr, result ) {
result.total += curr.item.qty;
},
initial: { total : 0 }
} )
역시 다음과 같이 array 형태로 결과가 출력된다:
[ { "ord_dt" : ISODate("2012-07-01T04:00:00Z"), "item.sku" : "abc123", "total" : 25 },
{ "ord_dt" : ISODate("2012-07-01T04:00:00Z"), "item.sku" : "abc456", "total" : 25 },
{ "ord_dt" : ISODate("2012-07-01T04:00:00Z"), "item.sku" : "bcd123", "total" : 10 },
{ "ord_dt" : ISODate("2012-07-01T04:00:00Z"), "item.sku" : "efg456", "total" : 10 },
{ "ord_dt" : ISODate("2012-06-01T04:00:00Z"), "item.sku" : "abc123", "total" : 25 },
{ "ord_dt" : ISODate("2012-06-01T04:00:00Z"), "item.sku" : "efg456", "total" : 15 },
{ "ord_dt" : ISODate("2012-06-01T04:00:00Z"), "item.sku" : "ijk123", "total" : 20 },
{ "ord_dt" : ISODate("2012-05-01T04:00:00Z"), "item.sku" : "abc123", "total" : 45 },
{ "ord_dt" : ISODate("2012-05-01T04:00:00Z"), "item.sku" : "abc456", "total" : 25 },
{ "ord_dt" : ISODate("2012-06-08T04:00:00Z"), "item.sku" : "abc123", "total" : 25 },
{ "ord_dt" : ISODate("2012-06-08T04:00:00Z"), "item.sku" : "abc456", "total" : 25 } ]
다음 예는 "ord_dt"가 "01/01/2012" 보다 큰(보다 최근의) 날짜인 것들 중 "keyf" 필드를 이용하여 이 중 "day_of_week"로 그룹화 하여 각 그룹에 대해 "qty" 값을 합산하고 개수를 파악하여 평균값을 구하는 것이다:
db.orders.group( {
keyf: function(doc) {
return { day_of_week: doc.ord_dt.getDay() } ; },
cond: { ord_dt: { $gt: new Date( '01/01/2012' ) } },
reduce: function ( curr, result ) {
result.total += curr.item.qty;
result.count++;
},
initial: { total : 0, count: 0 },
finalize: function(result) {
var weekdays = [ "Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday",
"Friday", "Saturday" ];
result.day_of_week = weekdays[result.day_of_week];
result.avg = Math.round(result.total / result.count);
}
} )
[Line 2 - 3] : "day_of_week"이라는 변수에 도큐먼트의 "ISODate"로 되어있는 "ord_dt"의 요일을 구한다. 이는 "getDay()" 함수를 이용하여 구할 수 있다. 예를 들어,
> var myOrder = db.orders.findOne({})
> myOrder
{
"_id" : ObjectId("5085a95c8fada716c89d0021"),
"ord_dt" : ISODate("2012-07-01T04:00:00Z"),
"ship_dt" : ISODate("2012-07-02T04:00:00Z"),
"item" : {
"sku" : "abc123",
"price" : 1.99,
"uom" : "pcs",
"qty" : 25
}
}
> myOrder.ord_dt.getDay()
0
숫자 "0"은 일요일, "1"은 월요일, "2"는 화요일, "3"은 수요일, "4"는 목요일, "5"는 금요일, "6"은 토요일을 의미한다. 결과값으로 "0"을 반환하였는데, 달력을 찾아보면 2012-07-01(2012년 7월 1일)이 일요일임을 확인할 수 있을 것이다.
즉, "keyf"의 역할은 각 도큐먼트의 "ord_dt"를 요일로 변환하는 것이다.
[Line 4] : "ord_dt"가 2012년 1월 1일 보다 최근의 날짜를 걸러내는 것이다. 참고로 "Date()" 함수는 주어진 날짜를 ISODate로 변환한다. 예를 들어 2013년 2월 18일에 대해
> var today = new Date('02/18/2014')
> today
ISODate("2014-02-17T15:00:00Z")
과 같이 ISO표준시로 변환한다. ISO 표준시에 대해 자세한 정보는 여기를 클릭.
[Line 5 - 8] : "reduce" 필드의 함수는 두 가지 인자를 취하는데, 첫번째 인자는 "현재 도큐먼트"를 두번째 인자는 "연산 결과의 도큐먼트"이다. "result.total += curr.item.qty"는 현재 도큐먼트의 "item" array에서 "qty" 값을 결과(result)의 total 값으로 누적 합산한다.
"result.count++"은 처리된 도큐먼트의 총 개수를 계산하기 위한 것이다.
[Line 9] : "total"과 "count" 값을 초기화한다.
[Line 10 - 16] : "result"를 함수의 인자로 취하고, "weekdays"라는 array형 변수에 Sunday(0)에서 Saturday(6)까지의 값을 할당하고, "result.day_of_week"에 요일값을 계산하여 저장하고, "result.total"을 "result.count"로 나누고 반올림하여 평균값을 구한 후 "result.avg"에 저장한다. 다음은 이에 대한 연산의 한 예이다:
[ { "day_of_week" : "Sunday", "total" : 70, "count" : 4, "avg" : 18 },
{ "day_of_week" : "Friday", "total" : 110, "count" : 6, "avg" : 18 },
{ "day_of_week" : "Tuesday", "total" : 70, "count" : 3, "avg" : 23 } ]
특히 "finalize" 필드를 finalizer라고도 하는데, finalizer는 사용자가 필요로 하는 최소의 정보(어떤 연산을 거쳐 얻은 결과값)만을 전달하여 데이터의 양을 최소화하는데 사용되며, 매우 중요하고 요긴하게 사용된다.
* 주의사항
- db.collection.group()은 sharded cluster(일종의 분산 데이터베이스 클러스터)에는 적용할 수 없다. 분산 데이터베이스 클러스터의 경우, 집합 프레임웍(aggregation framework) 또는 맵리듀스(map reduce)를 사용한다.
- 결과 세트는 최대 BSON 도큐먼트 사이즈 이내에서만 허용된다.
'Data Science > MongoDB' 카테고리의 다른 글
[MongoDB] Advanced Topics / DB Commands 정리 (0) | 2014.03.04 |
---|---|
[MongoDB] Aggregation / Map Reduce (0) | 2014.03.02 |
[MongoDB] Query / $snapshot (0) | 2014.02.09 |
[MongoDB] Query / Other Query Operations (0) | 2014.02.08 |
[MongoDB] Query / skip 속도 높이기 (0) | 2014.02.06 |