EDA Project - 데이터 분석
Data Analyst

빅데이터 관련 자료/[Project ]Brazil E-commerce

EDA Project - 데이터 분석

carpe08 2021. 7. 27. 15:32
320x100
320x100

EDA Project 프로젝트 기간: 7월 14일 ~ 8월 2일

팀명: 플로우

멘토: 윤00(머신러닝 엔지니어)

팀원: 최00, 박상욱, 홍00, 김00

담당매니저: 김00

개최: NanoDegree


4. 데이터 분석

전처리 된 데이터를 가지고 본격적으로 데이터 분석을 시작하였다.

내가 한 분석을 위주로 설명을 하겠다.

4.1 날짜, 시간, 요일별 소비 패턴 파악하기

분석 의도

  • 광고는 많은 사람들에게 노출이 되어야 그 효과를 제대로 발휘 할 수 있다. 브라질의 국민들이 어떤 요일에, 어떤 시간대에 주로 주문을 하는지를 파악한다면, 언제 광고를 노출 시켜야 가장 효율적인 홍보 효과를 누릴 수 있는지를 파악하고자 하였다.
  • 2016년부터 2018년까지 전체적인 브라질 상품 주문량의 증감 추이를 시계열로 분석하여 보다 종합적인 분석을 하고자 하였다.

분석

  • 날짜별 주문 트렌드는 어떨까?

날짜별 주문건수 시각화

 2017년 하반기 주문 건수가 급격히 증가하였다.

단체 주문 건이 있었는지 확인해본 결과 없었으며, 확인 결과 2017-11-24 일 Black Friday로 일시적으로 증가하였다.

 

참고 자료: https://www.cnet.com/tech/mobile/when-is-black-friday-2017/

 

Black Friday 2017 might come when you don't expect it

Killer deals are already here in some stores. Here's when Black Friday starts -- and it's not necessarily the day after Thanksgiving.

www.cnet.com

 

※ code

# (날짜)별 주문건수 계산
date_count =df['order_date'].value_counts()

# 한눈에 보기 위해 subplot 사용 
plt.rc('xtick',labelsize=10)
plt.rc('ytick',labelsize=10)
figure, (ax1, ax2) = plt.subplots(nrows=1, ncols=2)     
figure.set_size_inches(15, 3)

date_count.plot(title = '날짜별 주문 트랜드', ax=ax1)
date_count.plot.box(ax=ax2)

 


  • 연도 - 월별 주문 트렌드는 어떨까?

연도 - 월별 주문건수 시각화
연도별 주문 건수 시각화

 연도-월별 주문 시각화를 보면 주문 트렌드가 점차적으로 증가하는 것처럼 보인다.

하지만, countplot을 통해 연도별로 사용해보면 2017년은 꾸준히 증가하였지만, 2018년은 증가보다는 유지 또는 하락하는 추세라고 볼 수 있다.

 

※ code

# (날짜)별 주문건수 계산
date_count =df['order_date'].value_counts()

# 한눈에 보기 위해 subplot 사용 
plt.rc('xtick',labelsize=10)
plt.rc('ytick',labelsize=10)
figure, (ax1, ax2) = plt.subplots(nrows=1, ncols=2)     
figure.set_size_inches(15, 3)

date_count.plot(title = '날짜별 주문 트랜드', ax=ax1)
date_count.plot.box(ax=ax2)
plt.figure(figsize = (14,3))

sns.countplot(data=df, x='month', hue='year').set(title = '월별 주문 트랜드')

 

 


  • 요일별 주문 건수는 어떨까?
    요일별 주문 건수 시각화
    연도- 요일별 주문 건수 시각화

 2017년, 2018년 모두 토요일과 일요일이 상대적으로 다른 요일보다 적게 주문된다. 

브라질은 배송이 토요일을 포함해 주말에는 운영을 하지 않는다고 한다. 일요일보다 토요일이 적은이유는 토요일과 일요일에 주문하나 평일에 배송이 되기 때문이라고 한다.

 

※ code

import datetime as dt

day_name_list = 'Monday/Tuesday/Wednesday/Thursday/Friday/Saturday/Sunday'
day_name_list = day_name_list.split('/')

df['day_name'] = df['order_purchase_time'].dt.day_name() # 요일명을 'day_name' 컬럼을 만들어 준다.
df['day'] = df['order_purchase_time'].dt.dayofweek # 요일(숫자형태)를 'day'라는 컬럼에 담아준다.

day_name_count = day_name_count[day_name_list]
day_name_count

day_name_count.plot.bar(rot=30 ,title = '요일별 주문 트랜드', figsize= (8,3))

plt.figure(figsize = (8,3))

sns.countplot(data=df, x= 'day', hue='year').set(title='연도&요일별 주문 트랜드')
plt.legend(loc='upper right')

 


  • 시간대별 주문 건수는 어떨까?
    시간대별 주문 건수 시각화

새벽 시간보다 오전 10시 이후부터 22시까지 주문이 활성화 되어있다. 주문 트렌드가 많은 시간에 접속한 소비자 수가 많기 때문에 광고를 집행하게 된다면 이 시간대에 주문하는 것이 효율적으로 보인다.

 

※ code

# 주문 시간 출력 (0~24시)
df['hour'] = df['order_purchase_time'].dt.hour

hour_count = df['hour'].value_counts().sort_index()

hour_count.plot(title='시간대별 주문 트랜드', figsize=(14,3))

 


  • 요일 - 시간대별 주문 건수는 어떨까?

 보통 평일에는 오후 2시부터 오후 4시까지 가장 활발하게 주문이 많이 되며, 토요일은 주문이 확실히 다른 요일보다 적음을 확인할 수 있고, 일요일은 특히 오후 8시쯤 주문이 많다고 보인다.

 

※ code

df['day_num'] = df['order_purchase_time'].dt.dayofweek # 요일(숫자형태) 값을 'day_num' 이라는 컬럼에 넣어준다

h = df.groupby(['day_num','hour'])['order_id'].count().unstack()

h.index=day_name_list #요일(숫자)를 요일명으로 바꾸는데 성공

plt.figure(figsize=(20,8))
sns.heatmap(data=h ,annot=True, cmap='Blues', fmt='.0f')

 


  • 월-요일-시간별 카테고리 주문 건수는 어떨까?

월별 카테고리 주문 건수

월별 카테고리 주문 건수


 

요일별 카테고리 주문 건수

요일별 카테고리 주문 건수


 

시간별 카테고리 주문 건수

시간별 카테고리 주문 건수

 

※ code

month_category = pd.pivot_table(data= df, index ='category_name_kor', columns='month', values='order_id', aggfunc='count')
month_category.T.style.background_gradient()

day_category = pd.pivot_table(data= df, index ='category_name_kor', columns='day', values='order_id', aggfunc='count')
day_category.T.style.background_gradient()

hour_category = pd.pivot_table(data= df, index ='category_name_kor', columns='hour', values='order_id', aggfunc='count').fillna(0)
hour_category.astype(int).style.background_gradient()

4.2 고객별로 집단은 어디에 많이 위치할까?

분석 의도

  • 각 고객별로 유사성이 높은 개체를 군집으로 묶어 그룹화하여 각 집단의 성격을 파악함으로써 데이터 전체의 구조에 대한 이해를 해보고자 했다.

분석

    • 고객별 군집화

각 카테고리별로 거래 가격(price)의 z-score를 내면 해당 데이터의 가격이 카테고리의 평균에 비해 어느정도로 높거나 낮은지를 확인 할 수 있게 하는 점수를 나타내어 정규화해준다.(price_level로 변수 할당)

price_level의 평균을 나타내보았다. 2에 가까울수록 값이 비싸고, 값이 0에 가까울수록 값이 싸다.

price level mean 히스토그램

 

price_level과 total_price을 0~1로 만들어 정규화한다.

 

주문한 카테고리별 개수를 세어 전체개수로 각각 나눠주면 전체 비율이된다.

 

※ code

df['z_score_in_category']=df.groupby('category_name_kor')['total_price'].transform(lambda x: ((x - x.mean()) / x.std()))
# z스코어 2에 가까울수록 좋음. 가운데는 0 , 공식은 (x-평균)/표준편차, 
# 거래 가격(price)의 z-score를 계산. 이는 해당 데이터의 가격이 카테고리의 평균에 비해 어느정도로 높거나 낮은지를 알 수 있게 하는 점수입니다.



def q02(x):
    return x.quantile(0.2) #z스코어 상위 20퍼에 속하는 것 찾기

def q80(x):
    return x.quantile(0.8)  #z스코어 상위 80퍼에 속하는 것 찾기

category_price_df = df.groupby('product_category_name_english').agg({'z_score_in_category': [q02, q80]})
category_price_df.head()

# 카테고리마다 가격 차이가 크기떄문에 z스코어를 먼저구하고 quantile하는 것이 중요


category_price_dict = category_price_df.to_dict()



def price_level(z_score, category):
    lower_bound = category_price_dict[('z_score_in_category', 'q02')][category]
    upper_bound = category_price_dict[('z_score_in_category', 'q80')][category]
    if z_score < lower_bound: #싸다
        return 0 
    else:
        if z_score > upper_bound: #비싸다
            return 2
        else:
            return 1
            

df['price_level'] = df.apply(lambda x: price_level(x['z_score_in_category'], x['product_category_name_english']), axis=1)


#시각화
df.groupby('customer_unique_id')['price_level'].mean().hist()


# 구매 활동성을 3-level로 구분, 실제로 많이 사용한다.
user_feature_df['order_id'] = user_feature_df['order_id'].apply(lambda x: 3 if x >= 3 else x)
user_feature_df['price_level'] = user_feature_df['price_level'] / 3


# log normalization 후, 0~1 사이의 값으로 다시 normalize , min - max ~> 0부터 1까지 만듦
# x-x.min/(x.max-x.min)
user_feature_df['total_price'] = np.log(user_feature_df['total_price'])
user_feature_df['total_price'] = (user_feature_df['total_price'] - user_feature_df['total_price'].min()) / (user_feature_df['total_price'].max() - user_feature_df['total_price'].min())


user_feature_df.columns = ['order_count', 'price_level', 'total_price', 'category_name_kor','lat','lng','state']


# 카테고리별 개수를 세서 전체 sum(counts.values())를 각각 나눠주면 전체 비율이된다.
from collections import Counter

def get_counter_ratio(x):
    counts = Counter(x)
    for item, count in counts.items():
        counts[item] /= sum(counts.values())
    return counts


user_feature_df['category_counter_ratio'] = user_feature_df['category_name_kor'].apply(lambda x: get_counter_ratio(x))
user_feature_df = user_feature_df.reset_index(inplace=False)


data = user_feature_df['category_counter_ratio'].tolist()
category_df = pd.DataFrame(data).fillna(0) #결측값을 0으로 채워 데이터프레임 형태로 나타낸다.

 


  • 군집 분석

군집 분석에 필요한 데이터셋을 생성했다. 

군집 분석

Elbow Method

 

Cluster 간의 거리의 합을 나타내는 inertia가 급격히 떨어지는 구간이 생기는데 이 지점의 K 값을 군집의 개수로 사용
inertia_속성으로 확인할 수 있다. 

 

 

K값을 찾기 위해 Elbow method 이용

Elbow method(그래프가 꺾이는 부분)를 통해 적절한 k의 값 3을 찾았다.

 

cluster를 3으로 설정하여 x축과 y축을 각각 price_level과 total_price로 설정하여 시각화를 하였다.

User Group 1 (초록색): 총가격, 가격수준이 낮은 수준인 군집

User Group 2 (빨간색): 총가격, 가격수준이 보통 수준인 군집

User Group 3 (파란색): 총가격, 가격수준이 높은 수준인 군집

 

군집화된 고객들 거주지 folium 시각화

군집화된 고객들 거주지 folium 시각화

대체로 User Group 2가 골고루 분포되어있고, User Group 3가 상대적으로 북동쪽 해안, User Group 1 동남쪽에 많이 분포해있음을 알수있다.

 

※ code

# K-means 군집 분석 수행

from sklearn.cluster import KMeans

# K-means train & Elbow method
X = kmeans_df[['price_level', 'total_price']]

k_list = []
cost_list = []

for k in range (1, 10):
    kmeans = KMeans(n_clusters=k).fit(X)
    interia = kmeans.inertia_
    print ("k:", k, "| cost:", interia)
    k_list.append(k)
    cost_list.append(interia)
    
plt.plot(k_list, cost_list)


# elbow method를 통해 n_cluster를 3으로 설정 
kmeans = KMeans(n_clusters=3).fit(X)
cluster_num = kmeans.predict(X)
cluster = pd.Series(cluster_num)
kmeans_df['cluster_num'] = cluster.values


#군집이 만들어지면 이를 시각화해본다.

plt.figure(figsize=(10,10))

plt.scatter(kmeans_df[kmeans_df['cluster_num'] == 0]['price_level'], 
            kmeans_df[kmeans_df['cluster_num'] == 0]['total_price'], 
            s = 50, c = 'red', label = 'User Group 1')
plt.scatter(kmeans_df[kmeans_df['cluster_num'] == 1]['price_level'], 
            kmeans_df[kmeans_df['cluster_num'] == 1]['total_price'], 
            s = 50, c = 'green', label = 'User Group 2')
plt.scatter(kmeans_df[kmeans_df['cluster_num'] == 2]['price_level'], 
            kmeans_df[kmeans_df['cluster_num'] == 2]['total_price'], 
            s = 50, c = 'blue', label = 'User Group 3')

plt.title('User Cluster by price_level, total_price')
plt.xlabel('price_level')
plt.ylabel('total_price')
plt.legend()
plt.show()




# 지도의 중심을 지정하기 위해 위도와 경도의 평균을 구한다. 
import folium

lat=kmeans_df["lat"].mean()
lng=kmeans_df["lng"].mean()


for i in kmeans_df.index:
    sub_lat = kmeans_df.loc[i, "lat"]
    sub_long = kmeans_df.loc[i, "lng"]
    
    title = f"{kmeans_df.loc[i, 'state']}"
    
    
    if kmeans_df.loc[i, "cluster_num"] == 0:
        color = "red"
    elif kmeans_df.loc[i, "cluster_num"] == 1:
        color = "green"
    elif kmeans_df.loc[i, "cluster_num"] == 2:
        color = "yellow"

    
    folium.CircleMarker([sub_lat, sub_long],
                        radius=3,
                        color=color,
                          tooltip=title).add_to(m)
m

 


4.3 카테고리별 주문 연관성이 있을까?

분석 의도

  • 상품에는 대체재와 보완재라는 개념이 있다, 한 상품의 가격이 상승(하락)하면 다른 상품의 수요가 증가(감소)할 때 서로 다른 두 상품을 (서로) 대체재라고 하고, 한 상품의 가격이 상승(하락)하면 다른 상품의 수요가 감소(증가)할 때 서로 다른 두 상품을 보완재라고 부른다. 상품간 어떤 품목들이 대체재이고 보완재인지에 대한 정보는 판매자와 소비자 쌍방에게 유용하게 작용하는 정보라 할 수 있다. 지지도를 이용한 카테고리별 연관성 분석을 통해 상품간의 연관성을 파악하고자 하였다.

분석

카테고리별 2개 이상 구매한 리스트

 

카테고리 별 지지도 비교 

카테고리 별 지지도 비교 

 

이 비율이 매우 작으면 항목집합 X에서 생성되는 연관규칙이 의미가 없을 가능성이 높다.

침대 욕조 테이블 살 확률 : 0.245

가구장식을 살 확률: 0.23

 

상품의 구매, 서비스 등 일련의 거래들 간의 규칙을 발견하기 위해 연관분석을 실행

 

- 신뢰도가 0.3이상인 항목만 보기

 

- 향상도가 1 이상인 항목만 보기

(침대 욕조 테이블) 과 (가구 장식)은 서로 연관이 있는 품목임을 확인하였다. 연관성 높은 상품 카테고리 교차 프로모션 전략을 짜면 좋을 것으로 보인다.

 

※ code

# customer_id 로 그룹화 한 후 카테고리 개수 카운트
order_df.groupby('customer_id')['category_name_kor'].count().sort_values(ascending=False).reset_index()

#결측값 제거
order_df1 = order_df.dropna()

user_transactions = order_df1.groupby('customer_unique_id')['category_name_kor'].unique().reset_index()

# 2개 이상 카테고리 구매한 사람 추출
user_transactions = user_transactions[user_transactions['category_name_kor'].map(len) > 1]

# 리스트 형식으로 생성
user_transactions['category_name_kor'] = user_transactions['category_name_kor'].apply(lambda x: x.tolist())

# apriori 알고리즘
purchase_list = user_transactions['category_name_kor'].tolist()

from apyori import apriori

results = list(apriori(purchase_list,
                       min_support=0.01, # 지지도 0.001
                       min_confidence=0.01, # 신뢰도 0.01 
                       min_lift=1.0, # 향상도 1.0
                       max_length=2)) # 길이 (a,b) 처럼 2개 이하
results

max=0
for result in results:
    if len(result.items) == 2:
        items = [x for x in result.items]
        print(items[0], "||", items[1], "\n지지도:",result.support, "\n")
        if result.support>max:
            max_item1=items[0]
            max_item2=items[1]
            max=result.support
print('최대 지지도 : ',max_item1, "||", max_item2, "\n지지도:",max)

#MLxtend 라이브러리 (Machine Learning 확장)

from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import apriori, association_rules

te = TransactionEncoder()
te_ary = te.fit(purchase_list).transform(purchase_list)

association_df = pd.DataFrame(te_ary, columns=te.columns_)

frequent_itemsets = apriori(association_df, min_support=0.05, use_colnames=True)
frequent_itemsets.sort_values("support",ascending=False)

# 신뢰도가 0.3이상인 항목만 보기
from mlxtend.frequent_patterns import association_rules
association_rules(frequent_itemsets, metric="confidence", min_threshold=0.3)

# 신뢰도가 1이상인 항목만 보기
association_rules(frequent_itemsets, metric='lift', min_threshold=1)

 
  • 카테고리 연관 분석

※ code

# (날짜)별 주문건수 계산
date_count =df['order_date'].value_counts()

# 한눈에 보기 위해 subplot 사용 
plt.rc('xtick',labelsize=10)
plt.rc('ytick',labelsize=10)
figure, (ax1, ax2) = plt.subplots(nrows=1, ncols=2)     
figure.set_size_inches(15, 3)

date_count.plot(title = '날짜별 주문 트랜드', ax=ax1)
date_count.plot.box(ax=ax2)

 

4.4 판매자별 상품 리뷰 분석

분석 의도

댓글은 구매자들이 판매자들에게 보내는 메시지이다. 그 메시지에는 편리한 점, 재구매 의사와 같은 긍정적인 내용도 있고, 불편한 점, 개선 해야할 점과 같은 부정적인 내용도 있다. 긍정적인 내용과 부정적인 내용을 결정하는 단어나 문장을 형태소 단위로 분석하여 수치화 한다면 수 만개에 달하는 댓글들을 수치화하여 분류 할 수 있을 것이라는 생각에 분석을 하게 되었다. 이렇게 얻어진 수치들을 점수화 하여 순위를 매긴다면 구매자들이 공통적으로 어떤 것에 만족감을 느끼고 있고 어떤 것에 불편함을 느끼고 있는지를 파악할 수 있을 것이고 이는 불량 판매자 관리, 거래의 질 향상과 같은 긍정적인 효과를 산출하는데 크게 이바지 하게 될 것이다.

분석

상위 판매자 워드클라우드

'훌륭한', '엄청난', '좋은', '배달', '빠른', '만족하는' 등 긍정적인 단어로 보아 상품의 질과 배달시간이 빠름으로써 고객들이 많이 만족하고 있다고 분석이 된다.

 

하위 판매자 워드클라우드

'전화', ' 대기', '배달', '없음', '끔찍한', '불완전한' 등 부정적인 단어가 많은것으로 보아 상품에 대한 문의 전화를 잘 받지않아 소통이 없거나 배달 대기가 길어지고 있는 불완전한 판매 시스템이라고 볼 수 있다.

 

 상위 판매자에게는 고객 서비스의 질이 높이고 있으므로, 판매 수수료 인하 이벤트를 고려하면 좋을 것 같다.

반대로, 하위 판매자는 고객 서비스의 질을 낮추고 있으므로 불만족스러운 상황이 지속될 시 판매 제재를 가하는 것이 좋아보인다.

 

 

※ code

import pandas as pd

review = pd.read_csv( "dataset/olist_order_reviews_dataset.csv")
payment = pd.read_csv("dataset/olist_order_payments_dataset.csv")
order = pd.read_csv("dataset/olist_order_items_dataset.csv")

#데이터 병합
review = order.merge(review, how='left', on='order_id')
review = review.merge(payment, how='left', on='order_id')

#review 재설정
review = review[['seller_id','review_score','review_comment_title','review_comment_message','payment_value']]

#모두 값이 없는 값 결측값 제거
review = review.dropna(how='all', subset=['review_comment_title', 'review_comment_message'])
review = review.dropna(how='all', subset=['payment_value'])

 #연 1000만원 이하는 일반 판매자라고 보기 힘들어서 1000만원 이상 판매자만 추출
review=review[review['payment_value']>1000]

#제목과 리뷰데이터를 합치다.
review=review.fillna('')
review['text']=review[['review_comment_title','review_comment_message']].apply(lambda x: f'{x[0]}{x[1]}',axis=1)

#판매자 아이디별 리뷰점수, 총액, 리뷰를 각각 평균, 합계, 조인으로 그룹화해준다.
review=review.groupby('seller_id')[['review_score','payment_value','text']].agg({'review_score': 'mean','payment_value':'sum','text':lambda x: ''.join(x)}).reset_index()

#상위 판매자 30명, 하위 판매자 30명
star_seller=review.sort_values(['review_score','payment_value'],ascending=False)[:30]
bad_seller=review.sort_values(['review_score','payment_value'],ascending=False)[-30:] 


#Tokenize 라이브러리로 리뷰를 형태소 단위로 분석
import nltk
from nltk.tokenize import RegexpTokenizer

tokenizer = RegexpTokenizer(r'\w+')
text_star_seller = " ".join(star_seller['text'].values.tolist())
text_bad_seller = " ".join(bad_seller['text'].values.tolist())

tokens_star_seller = tokenizer.tokenize(text_star_seller)
tokens_bad_seller = tokenizer.tokenize(text_bad_seller)

#불용어 사전을 이용하여 쓸모없는 문구를 제거하기=
import nltk
from nltk.corpus import stopwords
nltk.download('stopwords')
tokens_star_seller

# 포루투갈어 불용어처리
stopwords_po= nltk.corpus.stopwords.words('portuguese')
stopwords_po[:10]

# 형태소 분석을 통해 얻은 리스트를 이용해 좋은 댓글과 나쁜 댓글을 분류하기
content_star_seller=[w for w in tokens_star_seller if (w not in stopwords_po) and (len(w)>1)]
content_bad_seller=[w for w in tokens_bad_seller if (w not in stopwords_po) and (len(w)>1)]


좋은 댓글 리스트와 나쁜 댓글 리스트에 포함되어 있는 형태소들의 빈도수를 파악하기

from collections import Counter
count_star_seller = Counter(content_star_seller).most_common(200)
count_bad_seller = Counter(content_bad_seller).most_common(200)

한글자로 된 단어는 제외하고 한글로 번역한 후 좋은 댓글과 나쁜 댓글로 구분하여 파악하기

from googletrans import Translator

trans = Translator()

ko_count_star_seller = []

for tup in count_star_seller:
    ko_count_star_seller.append((trans.translate(tup[0], src='pt', dest='ko').text, tup[1]))

ko_count_bad_seller = []

for tup in count_bad_seller:
    ko_count_bad_seller.append((trans.translate(tup[0], src='pt', dest='ko').text, tup[1]))
# 한글자 단어 제거

star_seller_count = [(tup[0], tup[1]) for tup in ko_count_star_seller if len(tup[0]) > 1]
bad_seller_count = [(tup[0], tup[1]) for tup in ko_count_bad_seller if len(tup[0]) > 1]

#워드 클라우드 설치
pip install wordcloud

from wordcloud import WordCloud
from wordcloud import STOPWORDS

#워드 클라우드 함수 정의
def wordcloud(data, width=1200, height=500):
    word_draw = WordCloud(
        font_path=r"C:\Windows\Fonts\malgun.ttf" ,
        width=width, height=height,
        stopwords=stopwords, 
        background_color="white",
        random_state=42
    )
    word_draw.generate(data)

    plt.figure(figsize=(15, 7))
    plt.imshow(word_draw)
    plt.axis("off")
    plt.show()

star_seller_count=str(star_seller_count)
star_seller_wordcloud=wordcloud(star_seller_count, width=1200, height=500)

bad_seller_count=str(bad_seller_count)
bad_seller_wordcloud=wordcloud(bad_seller_count, width=1200, height=500)

#워드클라우드 시각화
star_seller_wordcloud
bad_seller_wordcloud

 

320x100
320x100