Posts 뽐뿌 특가 데이터 분석 하기
Post
Cancel

뽐뿌 특가 데이터 분석 하기

나는 현명한 소비자가 되기 위해 항상 최적의 가격과 가성비 있는 상품을 찾는 데 큰 관심을 가지고 있다. 이러한 관심은 소비 습관을 개선하고 특별한 노하우를 가지게 되는데 큰 도움을 주었다. 데이터 사이언티스트로서 이 노하우를 활용하여 대표적인 특가 정보 커뮤니티인 ‘뽐뿌’의 특가 데이터를 분석하려고 한다. ‘뽐뿌’는 연간 약 2만 5천개의 특가 게시물이 공유되며, 수많은 소비자들이 정보 교환의 장으로 이용하는 대표적인 커뮤니티이다. 특가 게시물은 많이 등록되지만 그 중 유용한 특가는 소비자의 선택(조회, 추천, 댓글의 수)을 받아 인기/핫 게시물 (이하 인기 게시물)이라고 볼 수 있다. 이는 해당 제품이 매력적인 특가이기 때문에 대다수의 소비자에게 선택 받았다는 것을 의미하기 때문이다. 그래서 이번 분석에서는 일반 게시물과 인기 게시물의 차이를 중점적으로 살펴보려고 한다. 이번 분석으로 “더 현명한 소비”를 위한 인사이트를 도출하고, 이를 통해 소비자들이 더 현명한 소비를 할 수 있는데 도움이 되었으면 한다.

용어정리

1
2
3
특가 게시물 : 일반, 인기 게시물을 모두 통칭하는 게시물
일반 게시물 : 특가 정보를 공유하는 게시물
인기 게시물 : 일반 게시물에서 조회수, 추천, 댓글 수가 많아 소비자의 선택을 받아 인기 게시물로 등록된 게시물

1. 목적

이 분석을 통해 “뽐뿌”의 일반 게시물과 인기 게시물 사이의 차이점을 명확히 파악하고자 한다. 이 차이점을 바탕으로 어떤 상품이나 정보가 소비자들에게 인기를 받고 있는지 알아보려고 한다. 이런 인사이트를 통해 소비자들이 더 현명한 구매 결정을 할 수 있도록 도움을 주는 것을 목적으로 한다.

2. 분석 순서

  1. 인기 게시물 특성
    • 인기 게시물의 특성을 일반 게시물과 비교하여 확인한다.
  2. 카테고리 분석
    • 인기 게시물이 어떤 카테고리에 많은지 파악한다.
    • 각 카테고리별로 얼마나 많은 게시물이 등록되었는지 확인한다.
  3. 키워드 분석
    • ‘kiwi’ 패키지를 이용하여 게시물의 텍스트에서 명사만을 추출한다.
    • 추출된 명사 중 상위 20개의 단어를 통계적으로 분석하여 일반 게시물과 인기/핫 게시물 간의 키워드 차이를 비교한다.
  4. 가격 분석
    • 일반 게시물과 인기 게시물의 상품 가격을 기술통계와 박스 플롯을 활용하여 비교 분석한다.
    • 배송비에 대한 비교 분석도 박스 플롯을 이용해 진행한다.
  5. 판매 채널 분석
    • 일반 게시물과 인기 게시물에서 자주 언급되는 판매 채널 상위 10개를 파악한다.
    • 상위 10개에 들지 못하는 판매 채널에서의 일반 게시물과 인기/핫 게시물의 비율 및 수를 확인한다.
  6. 시계열 분석
    • 일반 게시물과 인기 게시물의 등록 패턴을 연도별, 월 별, 일별, 요일별, 시간별로 분석한다.

3. 결론

  • 소비자들은 유용하다고 생각하는 인기 게시물은 일반 게시물에 비해 많이 조회하고, 이를 추천하며 댓글을 활발하게 남기는 경향이 있다. 특히 추천 수는 인기 게시물에 대한 소비자의 만족도를 반영하는 중요한 지표로 작용한다.
  • 뽐뿌에서는 상품권과 식품/건강 카테고리는 인기 게시물이 상당히 많은 반면, 화장품, 육아, 등산/캠핑, 그리고 서적 관련 카테고리에서는 인기 게시물이 상대적으로 부족하다. 이러한 카테고리의 제품을 찾고 있는 사용자는 다른 정보 소스를 찾는 것이 더 도움이 될 것이다.
  • 뽐뿌의 사용자들은 코로나와 관련된 제품, 식품, 그리고 상품권에 특히 관심을 많이 보인다. 반면에 비싼 가전 제품들은 상대적으로 관심이 덜한 편이다.
  • 대다수의 뽐뿌 사용자들은 100,000원 이하의 저렴한 가격대의 제품을 선호하는 것으로 보이며, 대부분의 특가 게시물은 무료 배송을 제공한다. 그러나 배송비가 인기 게시물에 큰 영향을 주는 것은 아니다.
  • 지마켓/옥션은 특가 정보가 활발하게 공유되는 판매 채널로, 많은 소비자들로부터 큰 관심을 받는다. 인터파크롯데는 전체 게시물 대비 인기 게시물의 비율이 상당히 높다. 11번가는 많은 특가 게시물 중 작은 비율로 인기 게시물로 등록된다.쿠팡, 네이버, 하이마트, 카카오는 각각의 특화된 제품 또는 서비스 카테고리에서 유용한 특가 정보를 제공할 가능성이 높다.
  • 2022년은 특가 게시물의 총 수는 크게 변하지 않았지만, 인기 게시물의 수는 눈에 띄게 감소했다. 이러한 변화의 원인으로 경제적 불황과 코로나의 장기화가 영향을 미쳤을 것으로 보인다.
  • 5월과 11월에는 지마켓/옥션의 스마일데이와 11번가의 그랜드십일절로 인해 특가 게시물이 집중적으로 게시되는 경향이 있다. 그리고, 특히 1일과 11일에는 티몬11번가의 십일절 때문에 특가 게시물이 많이 게시되며, 인기 게시글도 많아 진다. 평일에는 특가 게시물이 주말에 비해 약 2배 더 많다. 아침 10시부터 11시 사이에 특가 게시물이 가장 활발하게 등록되며, 새벽 3시부터 4시 사이에는 가장 적게 등록된다.

4. 현명한 소비를 위한 전략적 제안

추천수를 주목하라

  • 추천수는 소비자들의 만족도와 제품의 가치를 반영하는 중요한 지표다. 높은 추천수를 받은 게시물에는 가치 있는 특가 정보가 있을 확률이 높다.

적극적인 정보 탐색

  • 상품권, 식품/건강 카테고리는 인기 게시물이 많으므로 뽐뿌를 적극 활용한다. 하지만 화장품, 육아, 등산/캠핑, 서적 등의 카테고리에서는 뽐뿌 보다는 다른 특가 정보 플랫폼을 활용하여 특가 제품을 찾아본다.

가격대 선택

  • 대부분의 사용자들은 100,000원 이하의 저렴한 제품을 선호한다. 따라서 이 가격대의 제품을 주로 찾아보는 것이 좋다. 무료 배송 제품을 우선적으로 찾는 것도 추천한다.

이벤트 및 프로모션 시기 활용

  • 5월과 11월, 그리고 1일과 11일에 특별한 이벤트나 할인이 진행된다. 이 기간 동안 쇼핑을 계획하여 가장 큰 혜택을 받을 수 있다.

판매 채널 선택

  • 지마켓/옥션, 인터파크, 롯데에서는 특가 정보가 활발하게 공유되므로 이 판매 채널들을 눈여겨 보면 좋을것 같다.

특가 게시물 등록이 활발한 시간대 이용

  • 오전 10시부터 11시 사이에는 특가 게시물이 가장 많이 등록된다. 이 시간대에 자주 방문하여 최신 특가 정보를 놓치지 않도록 한다.

이렇게 소비자로서 정보를 스마트하게 활용하고, 다양한 플랫폼과 이벤트를 잘 활용한다면, 훨씬 더 현명한 소비를 할 수 있을 것이다.

5. 상세 분석 및 코드

Package and Data load

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
import numpy as np
import re
import matplotlib.pyplot as plt
import seaborn as sns
import pingouin as pg
import ast
from PIL import Image

from scipy.stats import shapiro, levene, mannwhitneyu, chi2_contingency, ttest_ind
from datetime import datetime
from tqdm import tqdm
from collections import Counter
from wordcloud import WordCloud
from kiwipiepy import Kiwi

# custom function
from utils import autolabel, remove_outliers, top_text
1
2
3
4
5
6
7
8
# 파라미터
sns.set(style="ticks")
plt.rcParams['font.family'] = 'NanumGothic'
custom_palette = ["skyblue", "lightgreen"]
%matplotlib inline
%config InlineBackend.figure_format='retina'
title_font_size = 14
sub_font_size = 12
1
2
# 전처리한 데이터 불러오기
data = pd.read_csv("./datas/2023-08-08 23:47:16.905195_preprocessing.csv")
1
data_info = data.info()
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
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 117980 entries, 0 to 117979
Data columns (total 20 columns):
 #   Column         Non-Null Count   Dtype  
---  ------         --------------   -----  
 0   item_no        117980 non-null  int64  
 1   writer         117980 non-null  object 
 2   title          117980 non-null  object 
 3   end            117980 non-null  bool   
 4   comment        117980 non-null  int64  
 5   date           117980 non-null  object 
 6   recommend      117980 non-null  int64  
 7   opposite       117980 non-null  int64  
 8   view           117980 non-null  int64  
 9   category       117980 non-null  object 
 10  URL            117980 non-null  object 
 11  pop            117980 non-null  bool   
 12  hot            117980 non-null  bool   
 13  sales_channel  117980 non-null  object 
 14  price          117980 non-null  object 
 15  product_price  96227 non-null   float64
 16  shipping_cost  103266 non-null  float64
 17  real_title     117950 non-null  object 
 18  keywords       117980 non-null  object 
 19  post_type      117980 non-null  object 
dtypes: bool(3), float64(2), int64(5), object(10)
memory usage: 15.6+ MB
Column설명
item_no게시물 번호
Author작성자
Title게시물 제목
end특가 종료 여부
Comments댓글 수
Date게시 날짜
recommend추천수
opposite반대수
view조회수
Category특가 제품이 속한 카테고리
URLURL
pop인기 게시물 여부
hot핫 게시물 여부
sales_channel판매 채널
price상품 가격/배송비
product_price상품 가격
shipping_cost배송비
real_title게시물 제목 (판매채널, 상품가격/배송비 제외)
keywords게시글 제목에서 추출된 명사 키워드
post_type일반, 인기/핫 게시물 여부

1. 인기 게시물 특성

특가 게시물의 인기 요소

  • 인기 있는 특가 게시물을 살펴보니, 소비자들이 게시물을 더 많이 본다는 것, 댓글을 많이 남긴다는 것, 그리고 추천을 많이 한다는 것을 확인했다. 이런 게시물을 소비자들이 ‘유용한 특가’로 생각하는것 을 알 수 있다.
  • 전체 게시물 중에는 73%가 일반 게시물이고, 27%만이 인기 게시물이다. 이것은 총 게시물 중 27%만이 진짜로 소비자들의 마음을 사로잡는 특가라는 것을 의미한다.
  • 숫자로 보면, 인기 게시물은 일반 게시물에 비해 조회수와 댓글은 약 2배, 특히, 추천수는 11배 많아 소비자의 만족도를 잘 반영하는 지표로 보인다. 이 차이는 통계적으로도 의미있는 차이로 나타났다. (P-value 0.05)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 일반 게시물과 인기 게시물의 비율
plt.figure(figsize=(8,4))
ax = sns.countplot(data=data, x="post_type", palette=custom_palette)

# 전체 게시물 수 계산
total = len(data)

# 각 바 위에 비율 표시
for p in ax.patches:
    percentage = '{:.1f}%'.format(100 * p.get_height() / total)
    x = p.get_x() + p.get_width() / 2
    y = p.get_y() + p.get_height() * 1.02
    ax.annotate((f"{int(p.get_height())}개 ({percentage})"), (x, y), ha='center', va='center', size=sub_font_size)

ax.set_xlabel('')
ax.set_ylabel('')
ax.set_xticklabels(["일반", "인기"])

plt.title("유형 별 게시물 비율", size=title_font_size, fontweight='bold')
sns.despine()
plt.show()

output_10_0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 실제로 일반 게시물과 인기 게시물이 조회수(view), 추천수(recommend), 댓글수(comment)의 평균 차이가 있는지 T-test 통계 검증을 진행한다.
# 단, 데이터 수가 약 12만개이므로 정규성, 독립성, 등분산성 확인은 생략한다.

general_data = data[data['post_type'] == 'general']
popular_hot_data = data[data['post_type'] == 'popular/hot']
metrics = ['view', 'recommend', 'comment']
for metric in metrics:
    _, p_value = ttest_ind(general_data[metric], popular_hot_data[metric])

    # 유의수준에 따라 결과 해석
    alpha = 0.05  # 유의수준 (보통 0.05)
    if p_value < alpha:
        print(f"p-value {p_value}가 유의수준 {alpha}보다 작으므로 일반 / 인기 게시물간의 {metric}의 평균 차이는 통계적으로 유의미합니다.")
    else:
        print(f"p-value가 유의수준보다 크므로 일반 / 인기 게시물간의 {metric} 통계적으로 유의미하지 않습니다.")
1
2
3
p-value 0.0가 유의수준 0.05보다 작으므로 일반 / 인기 게시물간의 view의 평균 차이는 통계적으로 유의미합니다.
p-value 0.0가 유의수준 0.05보다 작으므로 일반 / 인기 게시물간의 recommend의 평균 차이는 통계적으로 유의미합니다.
p-value 0.0가 유의수준 0.05보다 작으므로 일반 / 인기 게시물간의 comment의 평균 차이는 통계적으로 유의미합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
avg_metrics = data.groupby('post_type')[['view', 'recommend', 'comment']].mean()

fig, axes = plt.subplots(nrows=3, ncols=1, figsize=(8, 12))
metrics = ['view', 'recommend', 'comment']
titles = ['평균 조회수', '평균 추천수', '평균 댓글수']

# 그래프
for ax, metric, title in zip(axes, metrics, titles):
    avg_metrics[metric].plot(kind='bar', ax=ax, rot=0, color=custom_palette)
    ax.set_title(title, size=title_font_size, fontweight='bold')
    ax.set_xlabel("")
    ax.set_xticklabels(["일반", "인기"])

    # 숫자 추가
    for bar in ax.patches:
        yval = bar.get_height()
        ax.text(bar.get_x() + bar.get_width()/2, yval, round(yval,2), ha='center', va='bottom', fontsize=sub_font_size)
        
sns.despine()
plt.tight_layout()
plt.show()

2. 카테고리 분석

상품권

  • 전체 게시물 중 상품권 카테고리는 4%이지만, 45.82%가 인기 게시물이다. 이는 상품권 특가에 대한 높은 관심을 반영한다. 상품권 특가를 찾는다면 뽐뿌를 자주 확인하는 것이 좋다.

식품/건강

  • 인기 게시물 비율은 32.23%. 전체 평균인 27%보다 높다. 이는 식품/건강 관련 특가에 대한 관심이 큰 것을 볼 수 있다.

의류/잡화, 디지털, 기타

  • 인기 게시물 비율은 약 26%로, 전체 평균(27%)에 거의 근접하다. 즉, 이 카테고리들은 뽐뿌에서 다른 카테고리와 비슷한 관심도를 받고 있으며 괜찮은 특가 정보를 발견할 수 있어 보인다.

가전/가구, 컴퓨터

  • 많은 게시물이 있지만, 인기 게시물의 비율은 15%와 18%로 평균보다 낮다. 이런 결과로 보아 뽐뿌에서 이 카테고리의 특가 정보가 큰 인기를 얻기는 어려운 것으로 보인다.

화장품, 육아, 등산/캠핑, 서적

  • 게시물과 인기 게시물의 비율이 모두 낮아, 뽐뿌에서 좋은 특가 정보를 찾는 것은 쉽지 않을 것 같다. 특가 정보가 필요하면 다른 커뮤니티를 확인하는 것이 좋을 것 같다.
1
2
3
4
5
6
7
8
# 데이터 프레임에서 게시물 종류 별 카테고리 갯수
category_counts = data.groupby(['post_type', 'category']).size().unstack(level=0).reset_index()
category_counts["total"] = category_counts["general"] + category_counts["popular/hot"]
category_counts["general_ratio"] = round(category_counts["general"] / category_counts["total"] * 100, 2)
category_counts["popular/hot_ratio"] = round(category_counts["popular/hot"] / category_counts["total"] * 100, 2)
category_counts["category"] = category_counts["category"].str.strip("[]")
category_counts = category_counts.sort_values(by="general_ratio", ascending=True)
category_counts
카테고리일반 게시물인기 게시물종합일반 게시물 비율인기 게시물 비율
상품권25682172474054.1845.82
식품/건강27209129404014967.7732.23
의류/잡화68342433926773.7526.25
디지털1148940481553773.9526.05
기타1763358802351374.9925.01
서적35611647275.4224.58
화장품2488755324376.7223.28
등산/캠핑80317998281.7718.23
컴퓨터69891544853381.9118.09
가전/가구74831413889684.1215.88
육아2279369264886.0613.94
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 순서 정렬
category_counts = category_counts.sort_values(by="general_ratio", ascending=False)

# 그래프 그리기
plt.figure(figsize=(12,4))
bar_general = plt.barh(category_counts["category"], category_counts["general_ratio"], color="skyblue", label="일반 게시물")
bar_popular_hot = plt.barh(category_counts["category"], category_counts["popular/hot_ratio"], left=category_counts["general_ratio"], color="lightgreen", label="인기 게시물")

# 그래프에 퍼센트 삽입
autolabel(bar_general)
autolabel(bar_popular_hot, previous_bars=[bar_general])

plt.xlabel("")
plt.ylabel("")
plt.legend(bbox_to_anchor=(1.05, 1))
plt.title("카테고리별 일반, 인기 게시물비율", size=title_font_size, fontweight="bold")
sns.despine()
plt.show()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
category_counts = category_counts.sort_values(by="general", ascending=True)

plt.figure(figsize=(12,4))
bar_general = plt.barh(category_counts["category"], category_counts["general"], color="skyblue", label="일반 게시물")
bar_popular = plt.barh(category_counts["category"], category_counts["popular/hot"], left=category_counts["general"], color="lightgreen", label="인기 게시물")

# 각 바 위에 수치 추가
for bar in bar_general:
    plt.text(bar.get_width() - 0.02 * bar.get_width(), bar.get_y() + bar.get_height()/2, 
             f'{int(bar.get_width())}', va='center', ha='right', color='black', fontsize=10)

for bar in bar_popular:
    plt.text(bar.get_x() + bar.get_width() + 0.02 * bar.get_width(), bar.get_y() + bar.get_height()/2, 
             f'{int(bar.get_width())}', va='center', ha='left', color='black', fontsize=10)

plt.xlabel("")
plt.ylabel("")
plt.legend(bbox_to_anchor=(1.05, 1))
plt.title("카테고리별 일반, 인기 게시물 갯수", size=title_font_size, fontweight="bold")
sns.despine()
plt.show()

3. 키워드 분석

공통 키워드 (주황색):

  • 마스크, 우유, 제로는 일반 게시물과 인기 게시물 양쪽에서 자주 나타난다.
  • 코로나19 때문에 마스크제로 키워드가 많이 등장하고, 우유는 일상생활에 꼭 필요한 항목이기 때문에 자주 나타나는 것으로 추측된다.

일반 키워드 (하늘색):

  • 모니터, 게이밍, 청소기는 주로 일반 게시물에서 보인다. 이는 앞에서 가전/가구 카테고리와 연관이 있어 보인다.
  • 이 제품들은 가격이 상대적으로 비싸거나, 뽐뿌 커뮤니티의 주 사용자 취향과는 조금 거리가 있어 인기 게시물로 많이 오르지 못하는 것 같다.

인기 키워드 (초록색):

  • 비비고, 컬쳐, 오뚜기, 동원 등의 키워드는 인기 게시물에서 주로 보인다.
  • 식품 관련 키워드인 비비고동원이 인기가 있고, 상품권 키워드인 컬쳐 역시 주목받고 있다. 이를 통해 뽐뿌 사용자들이 식품과 상품권 특가에 관심이 많다는 것을 알 수 있다.
1
data['keywords'] = data['keywords'].apply(ast.literal_eval)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 일반, 인기 게시물의 키워드 추출
pop_hot_keywords = [keyword for keywords_list in data[data['post_type'] == 'popular/hot']['keywords'] for keyword in keywords_list]
general_keywords = [keyword for keywords_list in data[data['post_type'] == 'general']['keywords'] for keyword in keywords_list]

# 키워드 언급 횟수
pop_hot_keyword_freq = Counter(pop_hot_keywords)
general_keyword_freq = Counter(general_keywords)

# 상위 20개 키워드 추출
top_pop_hot_keywords = pop_hot_keyword_freq.most_common(20)
top_general_keywords = general_keyword_freq.most_common(20)

# 상위 20개 키워드만 추출
top_20_pop_hot_keywords = set([keyword for keyword, _ in top_pop_hot_keywords])
top_20_general_keywords = set([keyword for keyword, _ in top_general_keywords])

# 상위 20개 키워드 중 공통, 인기, 일반 키워드 추출
unique_pop_hot_keywords_top_20 = top_20_pop_hot_keywords - top_20_general_keywords
unique_general_keywords_top_20 = top_20_general_keywords - top_20_pop_hot_keywords
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
# 공통, 인기, 일반 키워드의 색상 지정
pop_hot_colors = ['lightgreen' if keyword in unique_pop_hot_keywords_top_20 else 'lightgreen' if keyword in unique_general_keywords_top_20 else 'salmon' for keyword, _ in top_pop_hot_keywords]
general_colors = ['skyblue' if keyword in unique_pop_hot_keywords_top_20 else 'skyblue' if keyword in unique_general_keywords_top_20 else 'salmon' for keyword, _ in top_general_keywords]

# 그래프 생성
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))

pop_hot_keywords_plot, pop_hot_frequencies_plot = zip(*top_pop_hot_keywords)
general_keywords_plot, general_frequencies_plot = zip(*top_general_keywords)

# 일반 게시물 그래프
bars1 = ax1.barh(general_keywords_plot, general_frequencies_plot, color=general_colors)
ax1.set_title('일반 게시물')
for bar, freq in zip(bars1, general_frequencies_plot):
    ax1.text(freq - 2, bar.get_y() + bar.get_height()/2, int(freq), 
             va='center', ha='right', color='black')
ax1.invert_yaxis()

# 인기 게시물 그래프
bars2 = ax2.barh(pop_hot_keywords_plot, pop_hot_frequencies_plot, color=pop_hot_colors)
ax2.set_title('인기 게시물')
for bar, freq in zip(bars2, pop_hot_frequencies_plot):
    ax2.text(freq - 2, bar.get_y() + bar.get_height()/2, int(freq), 
             va='center', ha='right', color='black')
ax2.invert_yaxis()

fig.suptitle("게시물 유형 별 상위 20개 키워드", size=title_font_size, fontweight="bold")
plt.tight_layout()
sns.despine()
plt.show()

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
# 키워드를 단일 list로 변경
all_keywords = [keyword for sublist in data['keywords'] for keyword in sublist]

# 리스트 to 시리즈
keywords_series = pd.Series(all_keywords)

# 마스크 이미지
uploaded_mask_image = Image.open("./images/mask_image.png").convert("L")  # Convert to grayscale
uploaded_mask = np.array(uploaded_mask_image)


# 워드 클라우드 생성
wordcloud_with_uploaded_mask = WordCloud(
    font_path='./font/NanumGothic.ttf',
    colormap="PuBu",
    background_color='white',
    mask=uploaded_mask,
    width=800, height=400,
    contour_width=0.5,
    contour_color='white',
    collocations=False
).generate_from_frequencies(keywords_series.value_counts())

# 워드 클라우드 표시
plt.figure(figsize=(12, 12))
plt.imshow(wordcloud_with_uploaded_mask, interpolation="bilinear")
plt.axis('off')
plt.savefig('./01_wordcloud.png', dpi=200)
plt.show()

output_21_0

4. 가격 분석

제품 가격

  • 상품들의 가격은 주로 100,000원 아래로 분포한다. 인기 게시물의 제품들은 일반 게시물보다 더 저렴한 편이다. 이것은 제품이 특별한 할인이나 프로모션으로 인하여 저렴한 가격에 판매되어 인기 게시물로 선정된 것을 반영한다.

배송비

  • 전체 특가 게시물 중 79%의 상품들은 무료 배송으로 무료 배송 제품의 비율이 유료 배송 제품보다 약 4배 더 많다. 인기 게시물에서는 무료 배송 제품이 0.7%p 정도 더 많이 등장하는데, 이 차이는 통계적으로는 의미가 있지만 실제 연관성은 약하다. 대부분의 특가 게시물이 무료 배송인 것을 고려하면, 배송비는 인기 게시물 선정에 크게 기여하지 않는 것으로 보인다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 게시물 유형별 가격 분포 시각화
plt.figure(figsize=(8, 4))
ax = sns.boxplot(x='post_type', y='product_price', data=data, palette=custom_palette, showfliers=False)

# 박스플롯의 중앙값을 숫자로 표시
medians = data.groupby(['post_type'])['product_price'].median().values
medians = [int(round(m, 2)) for m in medians]
median_labels = [str(np.round(s, 2)) for s in medians]

pos = range(len(medians))
for tick, label in zip(pos, ax.get_xticklabels()):
    ax.text(pos[tick], medians[tick] + 5000, f"{median_labels[tick]}원", 
            horizontalalignment='center', size='small', color="black", weight='semibold')

plt.title('게시물 유형별 가격 분포', size=14, fontweight="bold")
sns.despine()
plt.xlabel("")
plt.ylabel('가격')
plt.xticks([0, 1], ['일반', '인기'])
plt.show()

1
2
3
4
5
6
7
8
9
10
11
12
13
# 실제로 일반 게시물과 인기 게시물이 조회수(view), 추천수(recommend), 댓글수(comment)의 평균 차이가 있는지 T-test 통계 검증을 진행한다.
# 단, 데이터 수가 약 12만개이므로 정규성, 독립성, 등분산성 확인은 생략한다.

general_prices = data[data['post_type'] == 'general']['product_price'].dropna()
popular_hot_prices = data[data['post_type'] == 'popular/hot']['product_price'].dropna()
_, p_value = ttest_ind(general_prices, popular_hot_prices)

# 유의수준에 따라 결과 해석
alpha = 0.05  # 유의수준 (보통 0.05)
if p_value < alpha:
    print(f"p-value ({round(p_value, 1)})가 유의수준 {alpha}보다 작으므로 일반 / 인기 게시물간 가격의 평균 차이는 통계적으로 유의미합니다.")
else:
    print(f"p-value가 유의수준보다 크므로 일반 / 인기 게시물간 가격은 통계적으로 유의미하지 않습니다.")
1
p-value (0.0)가 유의수준 0.05보다 작으므로 일반 / 인기 게시물간 가격의 평균 차이는 통계적으로 유의미합니다.
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
# 무료 배송과 유료 배송 구분
data['free_shipping'] = data['shipping_cost'] == 0

# 게시물 유형별 무료 배송과 유료 배송의 비율 계산
shipping_distribution = data.groupby('post_type')['free_shipping'].value_counts(normalize=True).unstack().fillna(0)

colors = ['salmon', 'salmon', 'skyblue', 'lightgreen']

# 스택드 바 차트로 시각화
ax = shipping_distribution.plot(kind='bar', stacked=True, figsize=(8, 6), width=0.8)
plt.title('게시물 유형별 무료/유료 배송 비율', size=14, fontweight="bold")
plt.ylabel('비율')
plt.xlabel('게시물 유형')
plt.xticks([0, 1], ['일반', '인기'], rotation=0)
plt.yticks([])

for p, color in zip(ax.patches, colors):
    p.set_facecolor(color)

for p in ax.patches:
    width, height = p.get_width(), p.get_height()
    x, y = p.get_xy() 
    ax.text(x + width/2, 
            y + height/2, 
            '{:.1%}'.format(height), 
            horizontalalignment='center', 
            verticalalignment='center',
            color='black')
sns.despine()
plt.legend(["유료 배송"], loc='upper right', bbox_to_anchor=(0.7, 0.48, 0.4, 0.5))
plt.show()

1
2
3
4
5
6
7
8
9
10
11
# 게시물 유형과 배송 유형 간의 교차 테이블 생성
data['shipping_type'] = data['shipping_cost'].apply(lambda x: '무료배송' if x == 0 else '유료배송')
contingency_table = pd.crosstab(data['post_type'], data['shipping_type'])

# 카이제곱 독립성 검정 수행
chi2, p, _, _ = chi2_contingency(contingency_table)

if p > 0.05:
    print("인기 게시물과 일반 게시물의 무료 배송 비율은 차이가 없습니다.")    
else:
    print("인기 게시물의 무료 배송 비율이 일반 게시물보다 많습니다.")
1
인기 게시물의 무료 배송 비율이 일반 게시물보다 많습니다.
1
2
3
4
5
6
7
8
9
10
11
12
# 표본의 크기가 크므로, 크래머 V를 통해 일반 게시물과 인기 게시물간 연관성 검증
n = contingency_table.sum().sum()
phi2 = chi2 / n
cramers_v = np.sqrt(phi2 / min(contingency_table.shape))

if cramers_v < 0.1:
    print("일반 게시물과 인기 게시물의 연관성은 아주 약합니다.")

elif cramers_v < 0.3:
    print("일반 게시물과 인기 게시물의 연관성은 보통입니다.")
else:
    print("일반 게시물과 인기 게시물의 연관성은 강합니다.")
1
일반 게시물과 인기 게시물의 연관성은 아주 약합니다.

5. 판매 채널 분석

지마켓/옥션

  • 총 35,548개의 게시물 중, 약 30.70%가 인기 게시물이다. 이는 지마켓/옥션에서 특가 정보가 활발하게 공유되고 있으며, 특가 게시물이 상당한 관심을 받는다는 것은 두 채널이 제공하는 제품 및 서비스의 다양성과 품질, 그리고 브랜드의 신뢰도와 연계될 수 있다. 이들 채널에서는 특가 정보를 찾는 소비자들이 상품의 품질과 가격에 만족하는 경우가 많아 인기 게시물의 비율이 높아진 것으로 추측할 수 있다.

인터파크, 롯데

  • 인터파크롯데는 전체 게시물 대비 인기 게시물의 비율이 높다. 이는 두 채널이 제공하는 특가 정보가 소비자들의 관심을 많이 받는다는 것을 의미한다. 따라서, 특가 정보를 찾는 소비자들에게 이 두 채널 또한 주요하게 고려될 만한 곳이다.

11번가

  • 전체 게시물 중 14,834개를 차지하며, 두 번째로 많은 특가 게시물이 있지만, 인기 게시물 비율은 22.40%로 상대적으로 낮다. 즉, 많은 특가 정보를 제공하지만 모두가 소비자의 인기를 끄는 것은 아니다.

쿠팡, 네이버, 하이마트, 카카오

  • 이들 채널의 게시물 비율은 상대적으로 낮지만, 특정 제품이나 카테고리에 특화된 특가 정보를 제공할 가능성이 높다. 하이마트는 가전, 쿠팡은 사용자의 구매/검색 이력 기반 할인, 카카오, 네이버는 제휴 브랜드 할인이다. 해당 판매 채널의 특화된 제품 또는 서비스에 관심 있는 소비자에게는 더 유용한 정보가 될 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Unknown을 제외한 상위 10개 판매 채널을 먼저 추출
top_10_channels_without_unknown = data[data['sales_channel'] != 'unknown'].groupby('sales_channel').size().nlargest(10).index.tolist()

# 판매 채널명 변경: 상위 10개에 포함되지 않거나 'Unknown'인 채널은 'ETC'로 변경
data['sales_channel_aggregated'] = data['sales_channel'].apply(lambda x: x if x in top_10_channels_without_unknown else 'ETC')

# 게시물 종류 별 판매 채널 갯수 계산
channel_counts_aggregated = data.groupby(['post_type', 'sales_channel_aggregated']).size().unstack(level=0).reset_index()
channel_counts_aggregated["total"] = channel_counts_aggregated["general"] + channel_counts_aggregated["popular/hot"]
channel_counts_aggregated["general_ratio"] = round(channel_counts_aggregated["general"] / channel_counts_aggregated["total"] * 100, 2)
channel_counts_aggregated["popular/hot_ratio"] = round(channel_counts_aggregated["popular/hot"] / channel_counts_aggregated["total"] * 100, 2)
idx = channel_counts_aggregated[channel_counts_aggregated['sales_channel_aggregated'] == "ETC"].index
channel_counts_aggregated.drop(idx , inplace=True)
channel_counts = channel_counts_aggregated.sort_values(by="general_ratio", ascending=True)
channel_counts = channel_counts.rename_axis(None, axis=1).reset_index(drop=True)
channel_counts.columns = ["판매 채널", "일반 게시물", "인기 게시물", "총합", "일반 게시물 비율", "인기 게시물 비율"]
channel_counts
 판매 채널일반 게시물인기 게시물총합일반 게시물 비율인기 게시물 비율
0지마켓/옥션24634109143554869.3030.70
1인터파크1636709234569.7730.23
2롯데31361169430572.8527.15
3티몬66862323900974.2125.79
4위메프69352312924775.0025.00
511번가1151133231483477.6022.40
6쿠팡41531090524379.2120.79
7네이버54821346682880.2919.71
8하이마트1313267158083.1016.90
9카카오1572300187283.9716.03
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
channel_counts = channel_counts_aggregated.sort_values(by="general_ratio", ascending=False)

plt.figure(figsize=(14,6))
bar_general = plt.barh(channel_counts["sales_channel_aggregated"], channel_counts["general_ratio"], color="skyblue", label="일반 게시물")
bar_popular_hot = plt.barh(channel_counts["sales_channel_aggregated"], channel_counts["popular/hot_ratio"], left=channel_counts["general_ratio"], color="lightgreen", label="인기 게시물")

autolabel(bar_general)
autolabel(bar_popular_hot, previous_bars=[bar_general])

plt.xlabel("비율")
plt.ylabel("판매채널별")
plt.legend(bbox_to_anchor=(1.0, 1.0))
plt.title("판매채널별 일반과 인기/핫 게시물의 비율", size=14, fontweight="bold")
sns.despine()
plt.show()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
channel_counts = channel_counts.sort_values(by="general", ascending=True)

plt.figure(figsize=(14,6))
bar_general = plt.barh(channel_counts["sales_channel_aggregated"], channel_counts["general"], color="skyblue", label="일반 게시물")
bar_popular = plt.barh(channel_counts["sales_channel_aggregated"], channel_counts["popular/hot"], left=channel_counts["general"], color="lightgreen", label="인기 게시물")

# 각 바 위에 수치 추가
for bar in bar_general:
    plt.text(bar.get_width() - 0.02 * bar.get_width(), bar.get_y() + bar.get_height()/2, 
             f'{int(bar.get_width())}', va='center', ha='right', color='black', fontsize=10)

for bar in bar_popular:
    plt.text(bar.get_x() + bar.get_width() + 0.02 * bar.get_width(), bar.get_y() + bar.get_height()/2, 
             f'{int(bar.get_width())}', va='center', ha='left', color='black', fontsize=10)

plt.xlabel("비율")
plt.ylabel("판매 채널")
plt.legend(bbox_to_anchor=(1, 1))
plt.title("채널별 일반, 인기, 핫 게시물의 갯수", size=14, fontweight="bold")
sns.despine()
plt.show()

6. 시계열 분석

연도

  • 2022년의 특가 게시물 수는 2019년과 유사하지만, 일반 게시물의 수가 크게 증가했다. 이는 더 많은 게시물이 인기를 얻지 못한 것으로, 경제적 요인이나 코로나 이후의 물가 상승 등의 영향인 것으로 보인다.

월별

  • 5월과 11월에 특가 게시물이 집중적으로 올라오는데, 이는 각각 지마켓/옥션의 스마일데이와 11번가의 이벤트 때문이다. 이 시기에는 특가와 관련된 인기 게시물도 증가하는 경향이 있다.

일별

  • 1일과 11일에 특가 게시물이 주목받는데, 티몬과 11번가의 십일절이 주요 요인이다. 특히 11일에는 11번가의 영향으로 지마켓/옥션에서의 게시물이 줄어든 것으로 추측된다.

요일별

  • 평일에 특가 게시물 및 인기 게시물의 수가 주말의 약 2배다. 따라서, 특가 제품을 찾을 때 주말보다 평일이 더 유리하다.

시간별

  • 오전 10~11시에 특가 게시물이 가장 많이 등록되며, 이후 점차 감소한다. 새벽 3~4시에는 가장 적게 등록되지만, 이 시간대의 일반 게시물과 특가 게시물 수가 유사하다. 이는 인기 게시물로 등록되는데 시간이 필요하기 때문으로 보인다.
1
2
# 날짜 형식 변환
data['date'] = pd.to_datetime(data['date'], format='%y.%m.%d %H:%M:%S')
1
2
3
4
5
6
7
8
# 연도별 분석
data['year'] = data['date'].dt.year
yearly_data = data[(data['year'] != 2018) & (data['year'] != 2023)]
yearly_analysis = yearly_data.groupby(['year', 'post_type']).size().unstack(level=1).fillna(0).reset_index()
yearly_analysis = yearly_analysis.rename_axis(None, axis=1).reset_index(drop=True)
yearly_analysis.columns = ["연도", "일반 게시물", "인기 게시물"]
yearly_analysis["종합"] = yearly_analysis["일반 게시물"] + yearly_analysis["인기 게시물"]
yearly_analysis
 연도일반 게시물인기 게시물종합
0201915938916225100
1202017459812425583
2202116593660123194
3202220857553026387
1
2
3
4
5
6
7
8
# 월별 분석
data['month'] = data['date'].dt.month
monthly_data = data[(data['year'] >= 2019) & (data['year'] <= 2022)]
monthly_analysis = monthly_data.groupby(['month', 'post_type']).size().unstack(level=1).fillna(0).reset_index()
monthly_analysis = monthly_analysis.rename_axis(None, axis=1).reset_index(drop=True)
monthly_analysis.columns = ["월", "일반 게시물", "인기 게시물"]
monthly_analysis["종합"] = monthly_analysis["일반 게시물"] + monthly_analysis["인기 게시물"]
monthly_analysis
 일반 게시물인기 게시물종합
01553126588189
12504225877629
23556627018267
34568223888070
45674127099450
56567823688046
67592224658387
78557823617939
89496621927158
910560021647764
10118551261811169
1112599022068196
1
2
3
4
5
6
7
# 일별 분석
data['day'] = data['date'].dt.day
day_analysis = data.groupby(['day', 'post_type']).size().unstack(level=1).fillna(0).reset_index()
day_analysis = day_analysis.rename_axis(None, axis=1).reset_index(drop=True)
day_analysis.columns = ["일", "일반 게시물", "인기 게시물"]
day_analysis["종합"] = day_analysis["일반 게시물"] + day_analysis["인기 게시물"]
day_analysis
 일반 게시물인기 게시물종합
01362713504977
12306210914153
23283210663898
34265310623715
45264310913734
56262310273650
67286911053974
78280610263832
89264810483696
910290410033907
1011394913385287
1112268610053691
1213279010023792
1314292310283951
1415290410693973
151628949433837
1617304611604206
1718284110783919
1819294310553998
1920283910633902
2021289610373933
2122300510714076
2223289610203916
232427609783738
242527409593699
2526279110953886
262724769063382
272824819813462
282922258703095
293019147602674
303114655622027
1
2
3
4
5
6
7
8
# 요일별 분석
data['weekday'] = data['date'].dt.day_name()
weekday_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
weekday_analysis = data.groupby(['weekday', 'post_type']).size().unstack(level=1).fillna(0).reindex(weekday_order).reset_index()
weekday_analysis = weekday_analysis.rename_axis(None, axis=1).reset_index(drop=True)
weekday_analysis.columns = ["요일", "일반 게시물", "인기 게시물"]
weekday_analysis["종합"] = weekday_analysis["일반 게시물"] + weekday_analysis["인기 게시물"]
weekday_analysis
 요일일반 게시물인기 게시물종합
0월요일15697554721244
1화요일15230504220272
2수요일14948527920227
3목요일13654459718251
4금요일13696518118877
5토요일669832039901
6일요일620830009208
1
2
3
4
5
6
7
# 시간 분석
data['hour'] = data['date'].dt.hour
hour_analysis = data.groupby(['hour', 'post_type']).size().unstack(level=1).fillna(0).reset_index()
hour_analysis = hour_analysis.rename_axis(None, axis=1).reset_index(drop=True)
hour_analysis.columns = ["시간", "일반 게시물", "인기 게시물"]
hour_analysis["종합"] = hour_analysis["일반 게시물"] + hour_analysis["인기 게시물"]
hour_analysis
 시간일반 게시물인기 게시물종합
00630336069909
11272017604480
2213308252155
336794831162
44423278701
55346276622
66537360897
7710796531732
8823249893313
99525516056860
10108088228110369
11118009213110140
1212486316086471
1313519715316728
1414545115537004
1515518915256714
1616473414566190
1717481913436162
1818369313445037
1919299712304227
2020287512684143
2121288912574146
2222288212754157
2323344912124661
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 각 선 그래프에서 가장 큰 값과 가장 작은 값을 표시하는 함수를 정의합니다.
def annotate_max_min(line, ax):
    xdata = line.get_xdata()
    ydata = line.get_ydata()
    max_index = np.argmax(ydata)
    min_index = np.argmin(ydata)
    if ydata[min_index] > 300:
        # 최대값 표시
        ax.text(xdata[max_index], ydata[max_index] + 100, int(ydata[max_index]), 
                ha='center', va='bottom', fontsize=9)

        # 최소값 표시
        ax.text(xdata[min_index], ydata[min_index] + 100, int(ydata[min_index]), 
                ha='center', va='bottom', fontsize=9)
    else:
        pass
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 그래프 그리기
fig, axes = plt.subplots(nrows=5, ncols=1, figsize=(12, 20))

# 연도별 그래프 ---------
axes[0].plot(yearly_analysis["연도"], yearly_analysis['일반 게시물'], label='일반', marker='o', color=custom_palette[0])
axes[0].plot(yearly_analysis["연도"], yearly_analysis['인기 게시물'], label='인기', linestyle='--', marker='o', color=custom_palette[1])


# 월별 그래프 ---------
axes[1].plot(monthly_analysis["월"], monthly_analysis['일반 게시물'], label='일반', marker='o', color=custom_palette[0])
axes[1].plot(monthly_analysis["월"], monthly_analysis['인기 게시물'], label='인기', linestyle='--', marker='o', color=custom_palette[1])

# 일별 그래프 ---------
axes[2].plot(day_analysis["일"], day_analysis['일반 게시물'], label='일반', marker='o', color=custom_palette[0])
axes[2].plot(day_analysis["일"], day_analysis['인기 게시물'], label='인기', linestyle='--', marker='o', color=custom_palette[1])

# 요일별 그래프 ---------
axes[3].plot(weekday_analysis["요일"], weekday_analysis['일반 게시물'], label='일반', marker='o', color=custom_palette[0])
axes[3].plot(weekday_analysis["요일"], weekday_analysis['인기 게시물'], label='인기', linestyle='--', marker='o', color=custom_palette[1])

# 시간별 그래프 ---------
axes[4].plot(hour_analysis["시간"], hour_analysis['일반 게시물'], label='일반', marker='o', color=custom_palette[0])
axes[4].plot(hour_analysis["시간"], hour_analysis['인기 게시물'], label='인기', linestyle='--', marker='o', color=custom_palette[1])

#그래프 숫자 작성
for i in range(0,5):
    for line in axes[i].get_lines():
        annotate_max_min(line, axes[i])
        
# 그래프 설정
axes[0].legend(["일반", "인기"], loc='upper right', bbox_to_anchor=(0.65, 0.7, 0.4, 0.5))

axes[0].set_title("연도 별 분석")
axes[1].set_title("월별 분석")
axes[2].set_title("일 별 분석")
axes[3].set_title("요일 별 분석")
axes[4].set_title("시간 별 분석")

axes[0].set_xlabel("")
axes[1].set_xlabel("")
axes[2].set_xlabel("월")
axes[3].set_xlabel("요일")
axes[4].set_xlabel("시간")

axes[0].set_ylabel("")
axes[1].set_ylabel("")
axes[2].set_ylabel("")
axes[3].set_ylabel("")
axes[4].set_ylabel("")

axes[0].set_xticks(range(2019, 2023))
axes[1].set_xticks(range(1, 13))
axes[2].set_xticks(range(1, 32))
axes[3].set_xticks(range(0,7))
axes[3].set_xticklabels(["월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일"])
axes[4].set_xticks(range(0, 25))

fig.suptitle("게시물 유형 별 시계열 분석", size=title_font_size, fontweight="bold")
plt.tight_layout(pad=1.7)
sns.despine()
plt.show()        

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 5월은 지마켓/옥션에서 11월은 지마켓/옥션과 11번가에서 각 스마일 데이와 그랜드 십일절 이라는 연례 특가 행사 이벤트를 한다. 이때문에 5월과 11월에 특가 게시물의 등록수가 많은것인지 확인한다.
# 11일은 11번가에서 십일절 이라는 특가 행사를하여 특가가 많은것인지 확인이 필요하다.
channel_counts = channel_counts_aggregated.sort_values(by="general_ratio", ascending=True)
order = list(channel_counts["sales_channel_aggregated"])
order.append("ETC")

# 월별 판매 채널 카운트
monthly_channel_counts = data.groupby([data['date'].dt.month, 'sales_channel_aggregated']).size().reset_index(name='counts')
monthly_channel_counts_pivot = monthly_channel_counts.pivot(index='date', columns='sales_channel_aggregated', values='counts').fillna(0)

# 비율로 변환
monthly_channel_percentage = monthly_channel_counts_pivot.divide(monthly_channel_counts_pivot.sum(axis=1), axis=0) * 100
monthly_channel_percentage = monthly_channel_percentage[order]
monthly_channel_percentage.rename_axis(None, axis=1).reset_index().round(2)
 지마켓/옥션인터파크롯데티몬위메프11번가쿠팡네이버하이마트카카오ETC
0132.131.882.888.239.5210.694.954.961.541.4821.74
1232.422.033.107.018.6011.944.805.781.431.4921.39
2328.591.923.387.887.6312.394.396.631.511.6724.02
3427.421.925.368.077.2012.374.976.241.451.9323.07
4539.701.532.917.585.5311.684.215.151.361.4118.96
5624.372.874.318.888.6012.284.937.311.302.0823.06
6729.441.753.477.629.3711.894.155.791.571.6123.33
7830.392.023.317.538.9712.183.535.001.231.6824.16
8930.852.222.757.037.1712.844.555.600.811.4024.78
91027.431.965.877.167.3511.044.335.670.751.4027.04
101131.531.832.896.295.9218.903.754.411.531.0021.94
111224.022.023.878.029.3211.854.536.911.231.8926.33
1
2
3
4
5
6
7
8
# 일별 판매 채널 카운트
daily_channel_counts = data.groupby([data['date'].dt.day, 'sales_channel_aggregated']).size().reset_index(name='counts')
daily_channel_counts_pivot = daily_channel_counts.pivot(index='date', columns='sales_channel_aggregated', values='counts').fillna(0)

# 비율로 변환
daily_channel_percentage = daily_channel_counts_pivot.divide(daily_channel_counts_pivot.sum(axis=1), axis=0) * 100
daily_channel_percentage = daily_channel_percentage[order]
daily_channel_percentage.rename_axis(None, axis=1).reset_index().round(2)
 지마켓/옥션인터파크롯데티몬위메프11번가쿠팡네이버하이마트카카오ETC
0130.241.233.0111.3510.0513.223.034.061.970.8421
1229.231.523.547.8710.4712.983.924.942.051.1122.37
2328.221.512.927.9510.8314.263.755.411.491.4622.19
3429.611.454.068.456.9713.894.54.981.351.2923.45
4531.921.344.587.668.4911.974.184.741.151.522.47
5631.811.123.6788.5812.714.585.181.261.3221.78
6730.121.614.787.578.2813.113.936.391.261.5621.39
7834.861.833.317.077.5710.594.26.131.441.5121.48
8931.031.953.447.717.9311.094.655.821.761.6822.94
91032.761.772.896.947.3512.444.665.581.131.8722.63
101121.171.441.784.295.939.972.863.50.931.3116.87
111231.512.332.557.298.7511.354.775.091.081.2524.03
121328.882.434.257.37.0912.535.276.541.211.6922.81
131431.512.333.248.257.1411.014.386.021.472.222.45
141532.372.323.47.48.1610.224.986.81.331.6121.42
151633.21.543.77.357.978.914.725.941.511.8223.33
161730.1211.673.265.756.897.24.595.681.071.3122.44
171831.771.684.757.557.458.854.216.051.791.6624.24
181933.421.533.837.279.485.2    
1
2
3
4
5
6
7
8
# 시간 별 판매 채널 카운트
hour_channel_counts = data.groupby([data['date'].dt.hour, 'sales_channel_aggregated']).size().reset_index(name='counts')
hour_channel_counts_pivot = hour_channel_counts.pivot(index='date', columns='sales_channel_aggregated', values='counts').fillna(0)

# 비율로 변환
hour_channel_percentage = hour_channel_counts_pivot.divide(hour_channel_counts_pivot.sum(axis=1), axis=0) * 100
hour_channel_percentage = hour_channel_percentage[order]
hour_channel_percentage.rename_axis(None, axis=1).reset_index().round(2)
시간지마켓/옥션인터파크롯데티몬위메프11번가쿠팡네이버하이마트카카오ETC
029.081.333.5214.5511.8118.292.612.690.821.3213.97
138.731.363.779.227.3414.913.062.901.090.6516.96
239.721.902.836.458.3112.903.903.811.070.7018.42
338.552.673.186.717.3111.964.133.101.030.6020.74
434.241.573.148.428.8411.703.853.991.280.5722.40
535.052.093.387.888.0414.313.544.021.290.9619.45
631.100.782.908.588.1412.934.684.461.670.7823.97
735.161.273.587.626.1811.897.103.641.730.5821.25
832.241.423.359.606.7913.225.983.621.391.2421.16
930.991.693.949.277.6212.224.684.421.461.7222.00
1030.892.704.046.637.9111.113.575.501.461.4024.80
1127.502.554.576.708.3812.323.445.711.381.4626.00
1229.352.244.337.378.1310.114.476.491.001.3425.17
1328.752.413.857.437.9211.864.066.421.651.1924.46
1427.282.114.216.947.9111.744.316.571.461.2826.19
1527.422.163.656.937.2114.344.605.941.581.1025.07
1627.242.133.306.917.5412.544.646.621.581.4126.09
1725.952.173.136.486.6911.054.469.061.314.5625.14
1826.621.773.206.477.7810.224.769.051.553.3425.23
1927.181.593.085.496.2713.745.969.301.372.3723.66
2028.531.673.266.576.5911.395.798.621.351.8824.35
2131.142.323.795.936.9711.246.736.221.421.6222.62
2233.221.713.015.927.4310.446.255.871.371.1323.65
2340.381.442.384.785.7912.985.514.250.971.1220.40
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
# 두 그래프를 2x1로 합치기
fig, axes = plt.subplots(3, 1, figsize=(15, 20))

# 월별 판매 채널 비율
sns.heatmap(monthly_channel_percentage, cmap='YlGnBu', annot=True, fmt='.2f', linewidths=.5, ax=axes[0], cbar=False)

# 일별 판매 채널 비율
sns.heatmap(daily_channel_percentage, cmap='YlGnBu', annot=True, fmt='.2f', linewidths=.5, ax=axes[1], cbar=False)

# 시간별 판매 채널 비율
sns.heatmap(hour_channel_percentage, cmap='YlGnBu', annot=True, fmt='.2f', linewidths=.5, ax=axes[2], cbar=False)


# 그래프 설정
axes[0].set_title("월 별")
axes[1].set_title("일 별")
axes[2].set_title("시간 별")

axes[0].set_xlabel("")
axes[1].set_xlabel("")
axes[2].set_xlabel("판매 채널")

axes[0].set_ylabel("월")
axes[1].set_ylabel("일")
axes[2].set_ylabel("시간")

fig.suptitle("판매 채널 게시물 등록 비율", size=14, fontweight="bold")
plt.tight_layout(pad=1.7)
sns.despine()
plt.show()

This post is licensed under CC BY 4.0 by the author.