다음의 가설이 참이라고 할 수 있는지 분석
가설: 셀프 주유소는 저렴하다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import platform
import seaborn as sns
import googlemaps
import time
import folium
import warnings
from urllib.request import urlopen, Request
from bs4 import BeautifulSoup
from selenium import webdriver
from tqdm import tqdm_notebook
from matplotlib import font_manager, rc
warnings.filterwarnings(action='ignore')
plt.rc('axes', unicode_minus = False)
%matplotlib inline
path = 'C:/Windows/Fonts/malgun.ttf'
if platform.system() == 'Darwin':
rc('font', family = 'Arial Unicode MS')
elif platform.system() == 'Windows':
font_name = font_manager.FontProperties(fname = path).get_name()
rc('font', family = font_name)
else:
print('Unknown system. sorry.')
서울시의 스타벅스 매장의 이름과 주소, 구 이름을 pandas data frame으로 정리
- 수집한 데이터들을 pandas data frame으로 정리
- 부가 정보 데이터는 Y/N로 저장
- 최종적으로 데이터 프레임에 들어가야할 col은 총 14개
- 1. 주유소명
- 2. 주소
- 3. 브랜드
- 4. 휘발유 가격
- 5. 경유 가격
- 6. 셀프 여부
- 7. 세차장 여부
- 8. 충전소 여부
- 9. 경정비 여부
- 10. 편의점 여부
- 11. 24시간 운영 여부
- 12. 구
- 13. 위도
- 14. 경도
url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome('../driver/chromedriver.exe')
driver.get(url)
# 주유소 사이트를 한 번 불러오면 opinet 홈페이지가 실행되기 때문에, 한 번 더 요청하여 싼 주유소 찾기 사이트로 이동
time.sleep(1)
driver.get(url)
sido_select = driver.find_element_by_css_selector('#SIDO_NM0')
sido_select.send_keys('서울')
driver.find_element_by_css_selector('#CWSH_YN').click()
driver.find_element_by_css_selector('#MAINT_YN').click()
driver.find_element_by_css_selector('#CVS_YN').click()
driver.find_element_by_css_selector('#SEL24_YN').click()
gu_select = driver.find_element_by_css_selector('#SIGUNGU_NM0')
gu_list = gu_select.find_elements_by_tag_name('option')
gu_names = [gu.get_attribute('value') for gu in gu_list]
gu_names = gu_names[1:]
gu_names, len(gu_names)
# 임의 '구' 선택: 마포구
gu_select = driver.find_element_by_css_selector('#SIGUNGU_NM0')
gu_select.send_keys(gu_names[12]) # 마포구
driver.find_element_by_css_selector('#searRgSelect > span').click()
# 조회 버튼 클릭
driver.find_element_by_css_selector('#searRgSelect > span').click()
# soup에 담기
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
# 마포구 주유소 개수 확인
oilStation_cnt = int(driver.find_element_by_css_selector('#totCnt').text)
oilStation_cnt
# 임의 주유소명 클릭
driver.find_element_by_css_selector('#body1 > tr:nth-child(1) > td.rlist > a').click()
# soup2에 담기
req2 = driver.page_source
soup2 = BeautifulSoup(req2, 'html.parser')
# 주유소 정보 리스트 확인
oilStation_list = soup2.select('#body1 > tr:nth-child(1) > td.rlist > a')
oilStation_list
# 주유소명, 휘발유 가격, 경유 가격 추출
name = soup2.find(id = 'os_nm').text
gasolinePrice = soup2.find('div', {'class':'gis_detail_info_bcon mgt_15'}).find('label', {'id':'b027_p'}).text
dieselPrice = soup2.find('div', {'class':'gis_detail_info_bcon mgt_15'}).find('label', {'id':'d047_p'}).text
name, gasolinePrice, dieselPrice
# 주소, 구, 브랜드 추출
address = soup2.find(id = 'rd_addr').text
gu = address.split()[1]
brand = soup2.find(id = 'poll_div_nm').text
address, gu, brand
# 부가정보: 이미지 색깔 표시 여부에 따라 Y/N 적용
# 셀프 여부
if soup2.find('tbody').find_all('tr')[0].find('span'):
if soup2.find('tbody').find_all('tr')[0].find('span').text == '셀프':
self = 'Y'
else:
self = 'N'
# 세차장 여부
if soup2.find(id = 'cwsh_yn').attrs['src'] == '/images/user/gis/oil_station_service1_01.gif':
wash = 'Y'
else:
wash = 'N'
# 충전소 여부
if soup2.find(id = 'lpg_yn').attrs['src'] == '/images/user/gis/oil_station_service1_02.gif':
lpg = 'Y'
else:
lpg = 'N'
# 경정비 여부
if soup2.find(id = 'maint_yn').attrs['src'] == '/images/user/gis/oil_station_service1_03.gif':
maint = 'Y'
else:
maint = 'N'
# 편의점 여부
if soup2.find(id = 'cvs_yn').attrs['src'] == '/images/user/gis/oil_station_service1_04.gif':
cvs = 'Y'
else:
cvs = 'N'
# 24시 영업 여부
if soup2.find(id = 'sel24_yn').attrs['src'] == '/images/user/gis/oil_station_service1_05.gif':
sel24 = 'Y'
else:
sel24 = 'N'
self, wash, lpg, maint, cvs, sel24
driver.close()
url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome('../driver/chromedriver.exe')
driver.get(url)
time.sleep(1)
driver.get(url)
sido_select = driver.find_element_by_css_selector('#SIDO_NM0')
sido_select.send_keys('서울')
driver.find_element_by_css_selector('#CWSH_YN').click()
driver.find_element_by_css_selector('#MAINT_YN').click()
driver.find_element_by_css_selector('#CVS_YN').click()
driver.find_element_by_css_selector('#SEL24_YN').click()
gu_select = driver.find_element_by_css_selector('#SIGUNGU_NM0')
gu_list = gu_select.find_elements_by_tag_name('option')
gu_names = [gu.get_attribute('value') for gu in gu_list]
gu_names = gu_names[1:]
gu_names[:5], len(gu_names)
oilStation_df = []
for gu in tqdm_notebook(gu_names):
# 구 선택
gu_select = driver.find_element_by_css_selector('#SIGUNGU_NM0')
gu_select.send_keys(gu)
time.sleep(1)
# 조회 버튼 클릭
driver.find_element_by_css_selector('#searRgSelect > span').click()
time.sleep(1)
# soup에 담기
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
soup
oilStation_cnt = int(driver.find_element_by_css_selector('#totCnt').text)
for i in range(1, oilStation_cnt+1):
# 주유소 클릭
driver.find_element_by_css_selector(f'#body1 > tr:nth-child({i}) > td.rlist > a').click()
time.sleep(1)
# soup2에 담기
req2 = driver.page_source
soup2 = BeautifulSoup(req2, 'html.parser')
soup2
# 주유소명, 휘발유 가격, 경유 가격
name = soup2.find(id = 'os_nm').text
gasolinePrice = soup2.find('div', {'class':'gis_detail_info_bcon mgt_15'}).find('label', {'id':'b027_p'}).text
dieselPrice = soup2.find('div', {'class':'gis_detail_info_bcon mgt_15'}).find('label', {'id':'d047_p'}).text
# 주소, 구, 브랜드
address = soup2.find(id = 'rd_addr').text
gu = address.split()[1]
brand = soup2.find(id = 'poll_div_nm').text
# 부가정보: Y/N
# 셀프 여부
if soup2.find('tbody').find_all('tr')[i-1].find('span'):
if soup2.find('tbody').find_all('tr')[i-1].find('span').text == '셀프':
self = 'Y'
else:
self = 'N'
# 세차장 여부
if soup2.find(id = 'cwsh_yn').attrs['src'] == '/images/user/gis/oil_station_service1_01.gif':
wash = 'Y'
else:
wash = 'N'
# 충전소 여부
if soup2.find(id = 'lpg_yn').attrs['src'] == '/images/user/gis/oil_station_service1_02.gif':
lpg = 'Y'
else:
lpg = 'N'
# 경정비 여부
if soup2.find(id = 'maint_yn').attrs['src'] == '/images/user/gis/oil_station_service1_03.gif':
maint = 'Y'
else:
maint = 'N'
# 편의점 여부
if soup2.find(id = 'cvs_yn').attrs['src'] == '/images/user/gis/oil_station_service1_04.gif':
cvs = 'Y'
else:
cvs = 'N'
# 24시 영업 여부
if soup2.find(id = 'sel24_yn').attrs['src'] == '/images/user/gis/oil_station_service1_05.gif':
sel24 = 'Y'
else:
sel24 = 'N'
datas = {
'name':name,
'gasolinePrice':gasolinePrice,
'dieselPrice':dieselPrice,
'address':address,
'gu':gu,
'brand':brand,
'self':self,
'wash':wash,
'lpg':lpg,
'maint':maint,
'cvs':cvs,
'sel24':sel24
}
oilStation_df.append(datas)
oilStation_df = pd.DataFrame(oilStation_df)
oilStation_df.tail(3)
oilStation_df['gu'].unique(), len(oilStation_df['gu'].unique())
driver.close()
# 위도, 경도 컬럼 추가
gmaps_key = '__________' # 본인이 발급받은 키값 입력
gmaps = googlemaps.Client(key = gmaps_key)
oilStation_df['lat'] = np.nan
oilStation_df['lng'] = np.nan
for idx, rows in tqdm_notebook(oilStation_df.iterrows()):
tmp = gmaps.geocode(rows['address'], language = 'ko')
if tmp:
lat = tmp[0].get('geometry')['location']['lat']
lng = tmp[0].get('geometry')['location']['lng']
oilStation_df.loc[idx, 'lat'] = lat
oilStation_df.loc[idx, 'lng'] = lng
else:
print(idx, rows['address'])
oilStation_df.tail(3)
# 휘발유와 경유 가격을 float형으로 변환하기 위해 천단위 콤마(,) 제거
for i in range(len(oilStation_df)):
oilStation_df['gasolinePrice'][i] = float(oilStation_df['gasolinePrice'][i].replace(',',''))
oilStation_df['dieselPrice'][i] = float(oilStation_df['dieselPrice'][i].replace(',',''))
oilStation_df.head(3)
oilStation_df.info()
콤마를 제거해 주었지만 type은 object
따라서 float로 변환해주고자 한다.
# 휘발유 가격, 경유 가격을 float로 변환
oilStation_df['gasolinePrice'] = oilStation_df['gasolinePrice'].astype('float')
oilStation_df['dieselPrice'] = oilStation_df['dieselPrice'].astype('float')
oilStation_df.info()
oilStation_df.head(3)
oilStation_df.to_csv('../data/EDA2_oil_station_data.csv', sep = ',', encoding = 'utf-8')
휘발유와 경유 가격이 셀프 주유소에서 정말 저렴한지 분석 결과 작성
- 분석 결과는 markdown으로 작성할 것
df = pd.read_csv('../data/EDA2_oil_station_data.csv', index_col = 0, encoding = 'utf-8')
df.tail(3)
시각화에 앞서 휘발유와 경유가 가장 싼 곳과 비싼곳을 순서대로 5군데를 보고자 한다.
# 휘발유 가장 비싼 주유소 5개
df.sort_values(by = 'gasolinePrice', ascending = False).head(5)
# 휘발유 가장 싼 주유소 5개
df.sort_values(by = 'gasolinePrice', ascending = True).head(5)
# 경유 가장 비싼 주유소 5개
df.sort_values(by = 'dieselPrice', ascending = False).head(5)
# 경유 가장 싼 주유소 5개
df.sort_values(by = 'dieselPrice', ascending = True).head(5)
plt.figure(figsize = (12, 8))
sns.set_style('darkgrid')
plt.grid(True)
plt.subplot(121)
sns.boxplot(x = 'self', y = 'gasolinePrice', data = df, palette = 'husl')
plt.subplot(122)
sns.boxplot(x = 'self', y = 'dieselPrice', data = df, palette = 'husl')
plt.show()
# 휘발유
plt.figure(figsize = (12, 8))
plt.grid(True)
sns.boxplot(x = 'brand', y = 'gasolinePrice', hue = 'self', data = df, palette = 'husl')
plt.show()
# 경유
plt.figure(figsize = (12, 8))
plt.grid(True)
sns.boxplot(x = 'brand', y = 'dieselPrice', hue = 'self', data = df, palette = 'husl')
plt.show()
# 휘발유
plt.figure(figsize = (20, 10))
plt.grid(True)
sns.boxplot(x = 'gu', y = 'gasolinePrice', hue = 'self', data = df, palette = 'husl')
plt.show()
# 경유
plt.figure(figsize = (20, 10))
plt.grid(True)
sns.boxplot(x = 'gu', y = 'dieselPrice', hue = 'self', data = df, palette = 'husl')
plt.show()
셀프여부와 브랜드에 따른 그래프를 참고하면 셀프주유소의 휘발유, 경유 가격은 보통주유소의 가격보다 낮게 형성되어 있는 것은 사실이다.
그러나, '구'별로 보았을 때는 해석이 달리 된다.
각 구에서 셀프주유소의 가격은 보통주유소보다 낮게 형성되어 있다고 볼 수 있으나,
특정 구(강남구, 강동구, 서초구)의 셀프주유소에서의 휘발유, 경유 가격은 특정 구(강서구, 관악구, 구로구, 금천구 등)의 보통주유소에서의 가격보다 높게 형성되어 있음을 확인할 수 있었다.
따라서 모든 셀프주유소의 휘발유, 경유 가격은 보통주유소보다 저렴하지는 않지만, 대체로 저렴하다고 볼 수 있다.