[EDA 과제] 02. 주유소 데이터 분석

svenskpotatis·2023년 9월 18일
0
  • 서울시 구별 주유소 데이터 크롤링으로 가져오기

문제 1) 수집한 데이터들을 pandas 데이터 프레임으로 정리

# 페이지 접근
url = 'https://www.opinet.co.kr/searRgSelect.do'
driver = webdriver.Chrome()
driver.get(url)
# 지역: 시/도

sido_list_raw = driver.find_element(By.ID, 'SIDO_NM0')
sido_list_raw.text
sido_list_raw.send_keys('서울') 
# 구

gu_list_raw = driver.find_element(By.ID, 'SIGUNGU_NM0')  # 부모 태그
gu_list = gu_list_raw.find_elements(By.TAG_NAME, 'option')  # 자식 태그

gu_names = [option.get_attribute('value') for option in gu_list]
gu_names = gu_names[1:]
# 구 검색
gu_list_raw = driver.find_element(By.ID, 'SIGUNGU_NM0')
gu_list_raw.send_keys(gu_names[0])
  • 데이터 수집
os_name = []  # 주유소명
os_add = []  # 주소
os_brand = []  # 브랜드
os_normPrice = []  # 휘발유 가격
os_disPrice = []  # 경유 가격
os_slf = []  # 셀프
os_cwsh = []  # 세차장
os_charge = []  # 충전소
os_maint = []  # 경정비
os_cvs = []  # 편의점
os_sel24 = []  # 24시
os_gu = []  # 구
os_lat = []  # 위도
os_lng = []  # 경도 
os_data = [
    os_name, os_add, os_brand, os_normPrice, os_disPrice, os_slf, os_cwsh, os_charge, os_maint, 
    os_cvs, os_sel24, os_gu, os_lat, os_lng]
for gu in tqdm_notebook(gu_names):
    # 구 검색
    element = driver.find_element(By.ID, 'SIGUNGU_NM0')
    element.send_keys(gu)
    time.sleep(1)

    html = driver.page_source
    soup = BeautifulSoup(html, 'html.parser')

    # 검색할 주유소 개수
    cnt = int(driver.find_element(By.ID, 'totCnt').text)
    
    for i in range(1, cnt+1):

        # 각 주유소 클릭
        station = driver.find_element(By.CSS_SELECTOR, f'#body1 > tr:nth-child({i}) > td.rlist > a')
        station.click()
        # time.sleep(1)

        html = driver.page_source
        soup = BeautifulSoup(html, 'html.parser')

        data = soup.select('#os_dtail_info')[0]

        # 이름
        os_name.append(data.select_one('.header').text.strip())

        # 주소
        os_add.append(data.select_one('#rd_addr').text)

        # 브랜드
        os_brand.append(data.select_one('#poll_div_nm').text)

        # 휘발유 가격
        os_normPrice.append(data.select_one('#b027_p').text)

        # 경유 가격
        os_disPrice.append(data.select_one('#d047_p').text)

        # 셀프
        slf = data.select_one('#SPAN_SELF_VLT_YN_ID')
        if type(slf.find('img')) == type(None):
            os_slf.append('N')
        else:
            os_slf.append('Y')

        # 세차
        cswh = data.select_one('#cwsh_yn')['src']
        if 'off' in cwsh:
            os_cwsh.append('N')
        else:
            os_cwsh.append('Y')

        # 충전소
        charge = data.select_one('#lpg_yn')['src']
        if 'off' in charge:
            os_charge.append('N')
        else:
            os_charge.append('Y')

        # 경정비
        maint = data.select_one('#maint_yn')['src']
        if 'off' in maint:
            os_maint.append('N')
        else:
            os_maint.append('Y')

        # 편의점
        cvs = data.select_one('#cvs_yn')['src']
        if 'off' in cvs:
            os_cvs.append('N')
        else:
            os_cvs.append('Y')

        # 24시 영업
        sel24 = data.select_one('#sel24_yn')['src']
        if 'off' in sel24:
            os_sel24.append('N')
        else:
            os_sel24.append('Y')

        # 구
        os_gu.append(gu)
os = pd.DataFrame(os_data).transpose()
os_columns = [
    'os_name', 'os_add', 'os_brand', 'os_normPrice', 'os_disPrice', 'os_slf', 'os_cwsh', 'os_charge', 'os_maint', 
    'os_cvs', 'os_sel24', 'os_gu', 'os_lat', 'os_lng']

위도, 경도 정보 넣기

import googlemaps
gmaps_key = "~"
gmaps = googlemaps.Client(key=gmaps_key)
  • 위도, 경도 정보 추가
for add in tqdm_notebook(os_add):
    tmp = gmaps.geocode(add, language='ko')
    lat = tmp[0].get('geometry')['location']['lat']
    lng = tmp[0].get('geometry')['location']['lng']

    os_lat.append(lat)
    os_lng.append(lng)
os['os_lat'] = os_lat
os['os_lng'] = os_lng

문제 2) 휘발유와 경유 가격이 셀프 주유소에서 정말 저렴한지 분석 결과 작성

  • 가격 데이터형 변환하기 object -> float
def priceFloat(price):
    price = price.replace(',', '')
    price = float(price)

    return price
os['os_normPrice'] = os['os_normPrice'].apply(priceFloat)
os['os_disPrice'] = os['os_disPrice'].apply(priceFloat)

시각화 - 그래프

import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import font_manager, rc

%matplotlib inline
rc('font', family='Arial Unicode MS')

분석 결과 1

  • boxplot - pandas
# boxplot pandas

os.boxplot(column='os_normPrice', by='os_slf');

  • boxplot - seaborn
# boxplot seaborn

sns.boxplot(x='os_slf', y='os_normPrice', data=os, palette='Set1')
plt.grid(True)
plt.show()

sns.boxplot(x='os_slf', y='os_disPrice', data=os, palette='Set2')
plt.grid(True)
plt.show()


휘발유와 경유 모두, 셀프 주유소가 셀프 주유소가 아닌 곳보다 가격이 높은 것을 볼 수 있다.

os['os_averagePrice'] = (os['os_normPrice'] + os['os_disPrice']) / 2
os.boxplot(column='os_averagePrice', by='os_slf');
os.boxplot(column='os_averagePrice', by='os_slf');
sns.boxplot(x='os_slf', y='os_averagePrice', data=os, palette='Set1')
plt.grid(True)
plt.show()

휘발유와 경유의 평균 값으로 그래프를 그려 보았다. 마찬가지로 셀프와 셀프가 아닌 주유소의 가격 차이를 볼 수 있다. 셀프 주유소는 대체로 셀프가 아닌 주유소보다 가격이 저렴하다.

지도

import json
import folium
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
  • 구 별 가격
import numpy as np

# 구 별 가격 - 휘발유
gu_data_norm = pd.pivot_table(data=os, index='os_gu', values='os_normPrice', aggfunc=np.mean)
gu_data_norm.head()
# 구 별 가격 - 경유
gu_data_dis = pd.pivot_table(data=os, index='os_gu', values='os_disPrice', aggfunc=np.mean)
gu_data_dis.head()
# 구 별 가격 - 평균
gu_data_av = pd.pivot_table(data=os, index='os_gu', values='os_averagePrice', aggfunc=np.mean)
gu_data_av.head()
geo_path = './02. skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))

my_map = folium.Map(location=[37.5502, 126.982], zoom_start=10.5, tiles='Stamen Toner')
my_map.choropleth(
    geo_data=geo_str,
    data=gu_data_norm,
    columns=[gu_data_norm.index, 'os_normPrice'],
    key_on='feature.id',
    fill_color='PuRd',
)
my_map

# swarmplot

plt.figure(figsize=(8, 6))
sns.swarmplot(x='os_slf', y='os_averagePrice', data=os, color='0', size=3)
plt.show()

분석 결과 2

가격이 높은 100개의 주유소 중 셀프 주유소가 얼마나 있는지 지도상으로 확인

my_map2 = folium.Map(location=[37.5502, 126.982], zoom_start=12, tiles='CartoDB positron')


for idx, rows in os.sort_values(by='os_averagePrice', ascending=False).head(100).iterrows():
    if rows['os_slf'] == 'Y':
        osColor = 'red'

    elif rows['os_slf'] == 'N':
        osColor = 'black'

    folium.Circle(
        location = [rows['os_lat'], rows['os_lng']],
        radius=1,
        color=osColor,
    ).add_to(my_map2)


my_map2


가격이 높은 순서대로 100개의 주유소를 정렬했을 때, 셀프 주유소가 셀프가 아닌 주유수보다 적은 것을 시각적으로 확인할 수 있다.

my_map3 = folium.Map(location=[37.5502, 126.982], zoom_start=12, tiles='CartoDB positron')


for idx, rows in os.sort_values(by='os_averagePrice', ascending=True).head(100).iterrows():
    if rows['os_slf'] == 'Y':
        osColor = 'red'

    elif rows['os_slf'] == 'N':
        osColor = 'black'

    folium.Circle(
        location = [rows['os_lat'], rows['os_lng']],
        radius=1,
        color=osColor,
    ).add_to(my_map3)


my_map3


가격이 낮은 순서대로 100개의 주유소를 정렬한 후, 셀프 여부를 살펴보았다. 셀프 주유소가 더 많은 것을 육안으로 확인할 수 있다.
추가적으로, 가격이 낮은 주유소들은 대체로 가격이 높은 주유소보다 외곽에 위치한다는 점을 알 수 있다.

0개의 댓글