[AIB 18기] Section 2 - Project(Do)

ysk1230·2023년 4월 13일
0

데이터 수집

네이버 오픈 API

오픈 API 초기 셋팅은

https://youtu.be/C8-SII6S4Bc

여기 주소를 참고해서 진행했다.

가설

카카오 스타일의 데이터 사이언스의 입장에서 네이버 쇼핑의 최저가를 확인하여 카카오 스타일 플랫폼에 올릴 품목의 최적가격을 위해 고려해야 할 요소는 무엇인가

최종적인 가설 설정을 위해 디테일한 조사가 필요하다고 판단 했다.

네이버 오픈 API를 이용하면 검색트랜드, 성별 기준 기기별 키워드 검색 등을 지원하는 것을 확인했다.

사전탐색 내용

  1. 어느 성별이 패션의류를 더 많이 검색할까?
  2. 어느 기기를 사용해서 검색할까?
  3. 최근에 검색된 패션의류 종류는 무엇이 있을까?

위 3개에 대한 조사를 진행하고 최종 가설을 수립하기로 결정 했다.


성별 기준 패션의류를 검색한 기기

* 네이버 Open API 인증 정보

client_id = "Hewe5vgmlLvWs_ubCDhs"
client_secret = "u8mu84aRwJ"

* 요청 헤더 설정

headers = {
'Content-Type': 'application/json',
'X-Naver-Client-Id': client_id,
'X-Naver-Client-Secret': client_secret
}

* 요청 URL

url = "https://openapi.naver.com/v1/datalab/shopping/category/keyword/device"

* 요청 Body

body = {
"startDate": "2021-01-01",
"endDate": "2022-12-31",
"timeUnit": "month",
"category": "50000000",
"keyword": "남성", # - > 여기서 여성으로 바꾸면 여성 데이터도 나온다
"device": "",
"gender": "m", # - > f : 여성
"ages": []
}

* API 호출

response = requests.post(url, headers=headers, data=json.dumps(body))

* 응답 데이터 확인

data = response.json()

* 데이터프레임화

results = data['results'][0]['data']
df_mdevice = pd.DataFrame(results, columns=['period', 'ratio', 'group'])
df_mdevice['category'] = '패션의류'
df_mdevice['gender'] = '남성'

* 차트 그래프 시각화

fig, ax = plt.subplots(figsize=(12, 6))

* seaborn 막대그래프 시각화

sns.barplot(data=df_mdevice, x='period', y='ratio', hue='group', ax=ax)

* x, y축 라벨링

ax.set_xlabel('일자')
ax.set_ylabel('비율')
plt.xticks(rotation=60)

* 그래프 제목 설정

title = '기기별 패션의류 검색(남성)'
ax.set_title(title)

* 범례 설정

ax.legend()
plt.show()


성별로 보는 패션의류 검색비율(모바일)

네이버 Open API 정보

client_id = "Hewe5vgmlLvWs_ubCDhs"
client_secret = "u8mu84aRwJ"

url = "https://openapi.naver.com/v1/datalab/shopping/category/gender"

headers = {
"Content-Type": "application/json",
"X-Naver-Client-Id": client_id,
"X-Naver-Client-Secret": client_secret,
}

body_f = {
"startDate": "2022-01-01",
"endDate": "2022-12-31",
"timeUnit": "month",
"category": "50000000",
"device": "mo",
"gender": "f"
}

body_m = {
"startDate": "2021-04-01",
"endDate": "2022-12-31",
"timeUnit": "month",
"category": "50000000",
"device": "mo",
"gender": "m"
}

response_f = requests.post(url, headers=headers, data=json.dumps(body_f))
result_f = response_f.json()

response_m = requests.post(url, headers=headers, data=json.dumps(body_m))
result_m = response_m.json()

df_f = pd.DataFrame(result_f['results'][0]['data'])
df_f = df_f[['period', 'ratio']]
df_f.columns = ['날짜', '여성비율']

df_m = pd.DataFrame(result_m['results'][0]['data'])
df_m = df_m[['period', 'ratio']]
df_m.columns = ['날짜', '남성비율']

df = pd.merge(df_m, df_f, on='날짜')

#시각화
plt.plot(df['날짜'], df['남성비율'], label='남성')
plt.plot(df['날짜'], df['여성비율'], label='여성')
plt.xlabel('날짜')
plt.ylabel('성별 비율')
plt.title('쇼핑 카테고리 성별 비율 추이(모바일)')
plt.xticks(rotation=60)
plt.legend()
plt.show()


네이버 쇼핑 API를 통한 트랜드 검색

* 클라이언트 아이디와 시크릿을 변수에 저장합니다.

client_id = "Hewe5vgmlLvWs_ubCDhs"
client_secret = "u8mu84aRwJ"

* 검색어와 필요한 데이터 개수, 정렬 순서 등을 변수에 저장합니다.

query = "패션의류"
display = 100
sort = "sim"

* API 요청을 보낼 URL을 생성합니다.

url = f"https://openapi.naver.com/v1/search/shop.json?query={query}&display={display}&sort={sort}"

* API 요청에 필요한 헤더 정보를 변수에 저장합니다.

headers = {
"X-Naver-Client-Id": client_id,
"X-Naver-Client-Secret": client_secret,
}

* API 요청을 보내고, 응답 데이터를 받습니다.

response = requests.get(url, headers=headers)
data = response.json()

* 데이터에서 모든 결과 컬럼을 추출합니다.

result = []
for i, item in enumerate(data["items"]):
rank = i + 1
title = item["title"]
link = item["link"]
image = item["image"]
lprice = item["lprice"]
hprice = item["hprice"]
mallName = item["mallName"]
productId = item["productId"]
productType = item["productType"]
brand = item["brand"]
maker = item["maker"]
category1 = item["category1"]
category2 = item["category2"]
category3 = item["category3"]
category4 = item["category4"]
result.append((rank, title, link, image, lprice, hprice, mallName, productId, productType, brand, maker, category1, category2, category3, category4))

* 추출한 데이터를 데이터 프레임으로 변환합니다.

df_top100 = pd.DataFrame(result, columns=["순위", "검색어", "링크", "썸네일 이미지", "최저가", "최고가", "쇼핑몰 이름", "제품 ID", "제품 타입", "브랜드", "제조사", "카테고리1", "카테고리2", "카테고리3", "카테고리4"])

* 0~255 범위의 RGB 색상값을 0~1 범위로 변환하는 함수

def rgb_to_float(rgb):
return tuple(map(lambda x: x/255.0, rgb))

* 데이터프레임(df_top100)에서 카테고리3 기준으로 집계합니다.

df_agg = df_top100.groupby("카테고리3").size().reset_index(name="개수")

* 카테고리3 기준으로 내림 차순으로 정렬합니다.

df_agg = df_agg.sort_values("개수")

* 막대 그래프를 그립니다.

colors = cm.viridis(df_agg["개수"] / max(df_agg["개수"])) # 색상값 생성
plt.barh(df_agg["카테고리3"], df_agg["개수"], color=colors)
plt.title("패션의류 top 100")
plt.show()


최저가 검색(최종)

client_id = "Hewe5vgmlLvWs_ubCDhs"
client_secret = "u8mu84aRwJ"
#위에서 구한 df_agg의 카테고리 3을 넣는다
queries = df_agg['카테고리3'].tolist()
idx = 0
display = 100
sort = 'dsc'
start = 1
end = 1000

shop_df = pd.DataFrame(columns=("Title", "Link", "Image", "Low price", "High Price", "Mall Name", "Product Id", "Product Type", "Maker", "Brand", "category1", "category2","category3","category4"))

for query in queries:
    for start_index in range(start, end, display):
        url = 'https://openapi.naver.com/v1/search/shop?query=' + urllib.parse.quote(query) \
              + "&display=" + str(display) \
              + "&start=" + str(start_index)

        request = urllib.request.Request(url)
        request.add_header('X-Naver-Client-Id', client_id)
        request.add_header('X-Naver-Client-Secret', client_secret)
        response = urllib.request.urlopen(request)
        rescode = response.getcode()
        if (rescode == 200):
            response_body = response.read()
            response_dict = json.loads(response_body.decode('utf-8'))
            items = response_dict['items']
            for item_index in range(0, len(items)):
                remove_tag = re.compile('<.*>?>')
                title = re.sub(remove_tag, '', items[item_index]['title'])
                link = items[item_index]['link']
                image = items[item_index]['image']
                low_price = items[item_index]['lprice']
                high_price = items[item_index]['hprice']
                mall_name = items[item_index]['mallName']
                product_id = items[item_index]['productId']
                product_type = items[item_index]['productType']
                maker = items[item_index]['maker']
                brand = items[item_index]['brand']
                category1 = items[item_index]['category1']
                category2 = items[item_index]['category2']
                category3 = items[item_index]['category3']
                category4 = items[item_index]['category4']
                shop_df.loc[idx] = [title, link, image, low_price, high_price, mall_name, product_id, product_type, maker, brand, category1, category2, category3 , category4]
                idx += 1
        else:
            print("Error Code:" + rescode)

     

위에서 정한 카테고리 1개당 1000개 씩 검색결과를 담고 데이터 프레임화 해주는 코드
원피스 검색결과 1000개를 담고, 티셔츠 1000개 담고 이런식으로 넘어간다.

총 18000건을 추출했다.


EDA & Feature Engineering

기초 작업

  1. 존재하지 않는 데이터 삭제

    * High Price와 category4 컬럼 삭제

    naver_shop = naver_shop.drop(['High Price', 'category4'], axis=1)

  2. 결측치 제거

    * Title 컬럼에서 결측치를 가지고 있는 행 추출

    null_rows = naver_shop[naver_shop['Title'].isnull()]

    * 결과 출력

    print(null_rows)

    * Title 행에서 결측치(null 값)을 가지고 있는 행 삭제

    naver_shop = naver_shop.dropna(subset=['Title'])
    naver_shop = naver_shop.reset_index(drop=True)

    * 결과 출력

    naver_shop.info()

이제 'Maker' 와 'Brand'의 결측치 처리가 필요하다.

Maker는 면직물을 만든 업체명을 의미하고 있으며(ex: 나이키, 신세계인터네셔널, 지오다노,삼성물산) 대기업 외 영세업체는 표기를 하지 않는 경우가 대다수인 것으로 확인

Brand는 면직물을 받아와 가공해서 브랜드를 붙여 파는 경우 표기가 되어있다. 우리가 백화점이나 쇼핑몰에서 흔히 보는 나이키,아이다스,톰보이, 지오다노 등등

따라서 만약 우리가 최저가를 서치하기 위해서는 maker가 영세한 곳에서 만들고 브랜드를 붙여파는 의류를 선택하는 판매 전략이 좋은지, maker, brand 둘다 없는 옷을 선택하여 걸어두는 것이 매출에 도움될지 확인이 필요하다.

  • Maker의 NUN 값은 'No-Maker' 로 변경
  • Brand의 NUN 값은 'NO-Brand' 로 변경

* Maker의 null 값을 'No-Maker'로 변경

naver_shop['Maker'] = naver_shop['Maker'].fillna('No-Maker')

* Brand의 null 값을 'NO-Brand'로 변경

naver_shop['Brand'] = naver_shop['Brand'].fillna('NO-Brand')

* 결과확인

naver_shop.info()

* 기초 전처리 완료 및 백업

naver_shop.to_csv('naver_shop_EDA_comp.csv', index=False)

* git hub 에 저장 후 경로로 불러오기

naver_shop = pd.read_csv('https://raw.githubusercontent.com/yskim1230/AIB_Section2-PJT_Modeling-Plan/main/naver_shop_EDA_comp.csv')


Low Price 컬럼을 범주형 형태 컬럼을 추가하여 분석에 도움이 되도록 할 예정이다.
범위 지정을 위해 금액 분포를 시각화 한다.

max_price = naver_shop['Low price'].max()
min_price = naver_shop['Low price'].min()

pd.set_option('display.float_format', lambda x: '%.2f' % x)
naver_shop.describe()

최저가 확인 결과 금액 분포가 매우 광범위한 것을 확인.

데이터를 확인해보니 샤넬이어서 그렇다

이상치는 아니지만 회귀 분석시 문제가 될 소지가 있기에 이번 프로젝트에서만 이상치라 간주하고 삭제한다.

삭제 기준은 IQR(interquartile range)을 이용한 이상치 제거를 사용한다. IQR은 데이터의 1사분위(Q1)와 3사분위(Q3)를 이용하여 계산되며, Q1 - 1.5 IQR 보다 작거나 Q3 + 1.5 IQR 보다 큰 값은 이상치로 간주하여 제거한다.

* 이상치 제거 함수

def remove_outliers(df, column):
q1 = df[column].quantile(0.25)
q3 = df[column].quantile(0.75)
iqr = q3 - q1
df = df[(df[column] >= q1 - 1.5 iqr) & (df[column] <= q3 + 1.5 iqr)]
return df

* 이상치 제거 적용

naver_shop = remove_outliers(naver_shop, 'Low price')
print(naver_shop.describe())

#1500여개 삭제
naver_shop.info()

* 최저가를 4개의 구간으로 나눔

max_price = naver_shop['Low price'].max()
min_price = naver_shop['Low price'].min()

* 4개의 구간으로 나눔

cut_points = np.linspace(min_price, max_price, num=5, endpoint=True)
label_names = ['Very Cheap', 'Cheap', 'Moderate', 'Expensive']
naver_shop['Price_range'] = pd.cut(naver_shop['Low price'], bins=cut_points, labels=label_names, include_lowest=True)
naver_shop

* 추가적으로 필요없는 컬럼 제거

naver_shop = naver_shop.drop(['Link', 'Image', 'Product Id'], axis=1)

추가 Feature Engineering

Brand와 Maker 카디널리티가 너무 높음
향후 회귀 분석시 one-hot encoding을 써도 과적합 문제 발생 가능성 다분
Maker Point / Brand Point 라는 컬럼 추가 예정
Maker란 제품을 만든 제조사를 의미하기에 현대에 와서는 크게 중요하지 않음. 있으면 1점, No-Maker면 0점
Brand가 현대에 와서는 더 중요한 의미(샤넬이냐 유니클로나는 매우 큰 차이), 유명 브랜드 기준 순위를 선정하여 순위 순서대로 3점, 2점 , 1점순으로 하락. No-Brand면 0점

'maker' 컬럼의 카디널리티 확인

maker_cardinality = len(naver_shop['Maker'].unique())
print('maker 카디널리티:', maker_cardinality)

'brand' 컬럼의 카디널리티 확인

brand_cardinality = len(naver_shop['Brand'].unique())
print('brand 카디널리티:', brand_cardinality)

'category1' 컬럼의 카디널리티 확인

category1_cardinality = len(naver_shop['category1'].unique())
print('category1 카디널리티:', category1_cardinality)

'category2' 컬럼의 카디널리티 확인

category2_cardinality = len(naver_shop['category2'].unique())
print('category2 카디널리티:', category2_cardinality)

'category3' 컬럼의 카디널리티 확인

category3_cardinality = len(naver_shop['category3'].unique())
print('category3 카디널리티:', category3_cardinality)

'Price_range' 컬럼의 카디널리티 확인

Price_range_cardinality = len(naver_shop['Price_range'].unique())
print('Price_range 카디널리티:', Price_range_cardinality)

* naver_shop['Maker Point'] 를 만들고 규칙은 naver_shop['Maker'] = No-Maker 면 0점 아니면 1로 입력

naver_shop['Maker Point'] = naver_shop['Maker'].apply(lambda x: 0 if x == 'No-Maker' else 1)

brand_point = pd.read_csv('https://raw.githubusercontent.com/yskim1230/AIB_Section2-PJT_Modeling-Plan/main/brand%20score.csv')

naver_shop = pd.merge(naver_shop, brand_point, on='Brand', how='left')
naver_shop['Brand Point'] = naver_shop['Score'].fillna(0)

여기서 실책, 브랜드 순위를 정확하게 나눌려면 통계자료를 기반으로 또는 그걸 제공하는 API를 찾아 연동했어야 하나 시간 부족으로 해당 작업을 진행하지 못했다.
브랜드의 경우 내가 아는 브랜드면 3점 아니면 2점,1점 이런식으로 부여했다. 당연 공신력이 떨어질 수 밖에 없다. 추후 시간이 되면 순위를 정확하게 나눈 데이터를 찾아 연동하는 작업을 하고싶다.

Price_range 점수 부여

price_range_score = {
'Very Cheap': 2,
'Cheap': 2,
'Moderate': 1,
'Expensive': 1
}
naver_shop['Price_range_score'] = naver_shop['Price_range'].map(price_range_score)

Total Point 계산

naver_shop['Total Point'] = naver_shop['Maker Point'] + naver_shop['Brand Point'] + naver_shop['Price_range_score']

Maker Point와 Brand Point 합계가 3 이상인 경우, Total Point에 1 추가

naver_shop.loc[(naver_shop['Maker Point'] + naver_shop['Brand Point']+ naver_shop['Price_range_score']) >= 5, 'Total Point'] += 1

EDA & Feature Engineering 종료

0개의 댓글