월별 매장별 매출을 집계하려면? 매장의 규모를 가늠하려면 어떻게 할까? 그룹 연산을 통해 초급/중급/고급으로 나누어서 살펴보도록 하자.
※ 본 내용은 monthly_store_transaction_data.csv 파일로 진행한다.
※ monthly_store_transaction_data.csv 다운받기 [클릭]
데이터를 준비하자. 대/중/소 카테고리는 cat_1/cat_2/cat_3 으로 되어있고 브랜드는 “brand” 접두사가 붙은 변수에 있다.
※ 정정합니다. prod_name이 제조사이고, brand_2가 제품명입니다.
1 | df = read.csv("monthly_store_transaction_data.csv") |
일단 매출 계산을 위해서 제품 판매 개수를 뜻하는 quantity 변수와 price 변수의 곱인 sales 변수를 만들어주자.
1 | df[, "sales"] = df$quantity * df$price |
초급
우선 1번 매장의 총 매출을 계산해보자.
1 | sum(df[df$store == 1, "sales"]) |
다른 매장도 계산해보자.
1 | sum(df[df$store == 1, "sales"]) |
숫자만 바뀌는 것을 보고 반복문으로 바꿀 수 있다는 생각을 했다면 반복문을 어느 정도 이해하고 감을 잡았다는 뜻이다. 다음과 같이 작성해보자.
1 | for(n_store in unique(df$store)){ |
그런데 각 숫자가 몇 번 매장인지 알기 어렵다는 것이 단점이다. 여기서 단순하게 “첫 번째가 1번 매장이겠지.” 라고 생각한다면 실무에서 지옥문이 열릴 수 있다.
다음과 같이 문자열을 이어 붙여주는 paste0()
함수를 활용해보자.
1 | for(n_store in unique(df$store)){ |
초심자에게는 문자열과 변수를 여러개 이어붙이는 것이 생소할 수 있으니 코드를 지우거나 바꿔가면서 원리를 파악하도록 하자.
중급
앞에서는 단순 출력만 했다. 하지만 계산 결과를 다른 곳에서도 활용할 수 있어야 하지 않을까? 이제 슬슬 객체로 저장해보자.
1 | sales = c() |
sales
객체에 저장했고 이를 출력해보았다. 이번에도 역시나 어떤 숫자가 어떤 매장과 연결되는지 알기 어렵다. 1차원 벡터에 새롭게 연산되는 숫자를 뒤에 하나씩 추가하는 방법 말고 데이터프레임에 정보를 저장해보자.
1 | df_sales = data.frame() |
비어있는 데이터프레임 df_sales
를 생성하고 연산 정보를 별도의 객체 df_sales_sub
객체에 저장하여 rbind()
함수로 데이터프레임을 차례대로 이어붙여주는 코드이다. 이 결과를 보고 충분히 만족하는 사람도 있겠지만, 데이터가 매우 커질 경우 반복문은 연산시간이 기하급수적으로 늘어난다. 이제 aggregate()
함수를 활용하여 연산해보자.
먼저 매장별 매출 집계이다.
1 | aggregate(data = df, sales ~ store, FUN = "sum") |
물결표시(~) 앞에 적은 변수가 FUN 인자에 선언한 함수의 영향을 받는다. 즉 sales 변수의 합계를 계산한다. 하지만 물결표시 뒤에도 변수가 선언되어있다. 즉 store 변수별 sales 변수의 합계가 되겠다.
연산 기준 추가는 +
부호를 활용한다. 다음은 월별 매장별 매출 합계를 산출하는 코드이다.
1 | aggregate(data = df, sales ~ month + store, FUN = "sum") |
그냥 심심풀이로 ggplot을 그려보았다. 향후 ggplot2 관련 포스팅에서 설명할 예정이며 포스팅 게시 이후 여기에 링크가 추가될 예정이다.
1 | library("ggplot2") |
역시나 dplyr가 빠질 수 없다. 매장별 매출 합계는 다음과 같다.
1 | library("dplyr") |
월별 매장별 매출 합계는 다음과 같다. 첫 번째 묶음 연산기준이 되는 변수를 가장 처음에 기술하고 다음 변수는 쉼표 이후에 기술한다.
1 | df %>% |
고급
이번엔 매장의 규모를 가늠할 수 있는 새로운 지표를 만들어보자. 새 지표의 기준은 다음과 같다고 가정한다.
대 카테고리 개수 $\times$ 10 + 중 카테고리 개수 $\times$ 5 + 소 카테고리 개수 $\times$ 1
우선 각 매장별 대 카테고리 개수 부터 구해보자.
1 | aggregate(data = df, cat_1 ~ store, FUN = function(x){length(unique(x))}) |
FUN 인자에 람다함수(일회성 함수)를 사용하였지만 더 쓸 예정이라 별도의 사용자 정의 함수로 만들어버리자.
1 | cat_counter = function(x){ |
슬슬 양산을 시작해보자.
1 | cat_1 = aggregate(data = df, cat_1 ~ store, FUN = "cat_counter") |
앞에서 제시되었던 공식대로 연산하고 그 결과를 확인해보자.
1 | idx_cal = (cat_1$cat_1 * 10) + (cat_2$cat_2 * 5) + cat_3$cat_3 |
아무튼 계산했다. 1번이 1690으로 가장 큰 값을 가진다. 그런데 좀 더 간결하게 할 수 없을까? 다음과 같이 aggregate()
함수의 변칙적인 사용법을 아는 사람은 많지 않다. 여기서 마침표는 수식(formula)에서 명시되지 않은 모든 변수를 뜻한다. 즉, store 변수만 명시되었으니 나머지 cat_1 , cat_2 , cat_3 변수가 여기에 지정된다.
1 | cat_123 = aggregate(data = df[, c(2, 5:7)], . ~ store, |
이걸 또 dplyr 패키지 버전으로 바꿔보자.
1 | df %>% |
갑자기 쉬워진 것 같지만 기분탓이다. 여기서는 n_distinct()
함수를 사용하는 것이 핵심이다. 이는 length(unique())
와 같이 length()
와 unique()
함수의 중첩 대신 사용할 수 있다.
최종 연산은 다음과 같다.
1 | df_dplyr[, "index"] = (df_dplyr$cat_1 * 10) + (df_dplyr$cat_2 * 5) + df_dplyr$cat_3 |
물론 인덱스 연산도 별도의 가중합 함수를 만들면 그 형태가 변하긴 하겠다. 그리고 가중합은 별도의 패키지에서 제공하긴 하는데 굳이 간단한 연산은 패키지에 의존하지 않고 직접 함수를 만들어서 사용하는 것이 코드 유지보수 및 관리에 좋다.