J o e
JoE's StOrY
J o e
  • 분류 전체보기 (206)
    • workSpace (184)
      • 도메인 지식 (2)
      • ALGORITHM (39)
      • ANDROID (3)
      • JS (0)
      • JAVA (21)
      • MYSQL (6)
      • NETWORK (3)
      • PYTHON (91)
      • LINUX (9)
      • PROJECT (4)
    • Others (20)
      • Opic (1)
      • myLife (17)
      • popSong (1)
      • 정보처리기사 (1)
    • 훈빠의 특강 (0)
      • opencv (0)
      • python (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • The code with long statements is⋯
  • 매일 매일이 행복하고 밝은 날이 될거에요

인기 글

태그

  • linearclassification
  • Python
  • DTO
  • 파이썬
  • 넘파이함수
  • MySQL
  • 태블릿 연동
  • java
  • sort_value
  • How to create a GUI in Java with JFrame?
  • full loss
  • 이미지 연산
  • 단어의 개수
  • 넘파이 문제
  • dao
  • sort_index
  • read_html
  • ㅖ43
  • Fully Connected Network
  • numpy

최근 댓글

최근 글

티스토리

J o e

WHY?

[Pandas][Numpy][seaborn] 2019년 서울 특별시 주유소 판매가격 분석 및 표 만들기
workSpace/PYTHON

[Pandas][Numpy][seaborn] 2019년 서울 특별시 주유소 판매가격 분석 및 표 만들기

2021. 1. 18. 19:17

 

 

데이터 분석¶

  • 데이터 정보 : 2019년도 서울에 위치하고 있는 주유소의 위치정보 및 일간 유가

(1) 고급휘발유 0인 값 108256개 / 실내등유 0인 값 110560개 / 전체 데이터 181714개
즉 고급휘발유와 실내등유를 보유하지 않은 주유소가 많다.

(2) 휘발유나 경유가 0인 데이터는 전체 데이터 대비 비율이 적기때문에 바로 삭제처리

(3) 도로명 column 추가

(4) 웹 데이터 크롤링

(5) 상,하반기 결합

(6) 기간 컬럼 dataTime으로 형변환 > 년 월 일 요일 컬럼 생성

In [1]:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
In [2]:
import platform

from matplotlib import font_manager, rc
plt.rcParams['axes.unicode_minus'] = False

if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    path = "c:/Windows/Fonts/malgun.ttf"
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('Unknown system... sorry~~~~')
 

[웹 데이터 크롤링]

In [3]:
# 한글문제 해결
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

import platform

path = 'c:/Windows/Fonts/malgun.ttf'
from matplotlib import font_manager, rc
if platform.system() == 'Darwin':
    rc('font', family = 'AppleGothic')
elif platform.system() == 'Windows':
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('Unknown system... sorry~~~')
 

1. 데이터 불러오기¶

 

상반기 주유소 판매 데이터 불러오기¶

In [4]:
first = pd.read_csv('../data/2019년 상반기 주유소 판매가격.csv',encoding='cp949')
first
Out[4]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190101 SK에너지 셀프 1673 1465 1365 0
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190102 SK에너지 셀프 1673 1465 1365 0
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190103 SK에너지 셀프 1673 1465 1365 0
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190104 SK에너지 셀프 1673 1465 1365 0
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190105 SK에너지 셀프 1673 1465 1365 0
... ... ... ... ... ... ... ... ... ... ... ...
90585 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20190626 GS칼텍스 셀프 0 1529 1389 0
90586 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20190627 GS칼텍스 셀프 0 1529 1389 0
90587 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20190628 GS칼텍스 셀프 0 1529 1389 0
90588 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20190629 GS칼텍스 셀프 0 1529 1389 0
90589 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20190630 GS칼텍스 셀프 0 1529 1389 0

90590 rows × 11 columns

In [5]:
second = pd.read_csv('../data/2019년 하반기 주유소 판매가격.csv',encoding='cp949')
second
Out[5]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190701 SK에너지 셀프 1777 1577 1477 0
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190702 SK에너지 셀프 1777 1577 1477 0
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190703 SK에너지 셀프 1777 1577 1477 0
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190704 SK에너지 셀프 1777 1577 1477 0
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190705 SK에너지 셀프 1777 1577 1477 0
... ... ... ... ... ... ... ... ... ... ... ...
91119 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191227 GS칼텍스 셀프 0 1540 1389 1100
91120 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191228 GS칼텍스 셀프 0 1540 1389 1100
91121 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191229 GS칼텍스 셀프 0 1540 1389 1100
91122 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191230 GS칼텍스 셀프 0 1540 1389 1100
91123 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191231 GS칼텍스 셀프 0 1540 1389 1100

91124 rows × 11 columns

In [6]:
# 상반기 판매가격 데이터 프레임 결측치 조회
first.isna().sum()
Out[6]:
번호       0
지역       0
상호       0
주소       0
기간       0
상표       0
셀프여부     0
고급휘발유    0
휘발유      0
경유       0
실내등유     0
dtype: int64
In [7]:
# 하반기 판매가격 데이터 프레임 결측치 조회
second.isna().sum()
Out[7]:
번호       0
지역       0
상호       0
주소       0
기간       0
상표       0
셀프여부     0
고급휘발유    0
휘발유      0
경유       0
실내등유     0
dtype: int64
 

[웹 데이터 크롤링]

In [8]:
from selenium import webdriver
In [9]:
pip install selenium
 
Requirement already satisfied: selenium in c:\anaconda3\lib\site-packages (3.141.0)
Requirement already satisfied: urllib3 in c:\anaconda3\lib\site-packages (from selenium) (1.25.11)
Note: you may need to restart the kernel to use updated packages.
In [10]:
#네이버에 접속
driver = webdriver.Chrome('chromedriver')
driver.get('http://naver.com')
In [11]:
# BeautifulSoup를 이용해서 페이지 내용 읽어오기
from bs4 import BeautifulSoup

html = driver.page_source # 현재 Selenium이 접근한 페이지 소스를 넘겨 받는다. 
soup = BeautifulSoup(html, 'html.parser')
In [12]:
driver.get('http://www.opinet.co.kr/searRgSelect.do')
driver.get('http://www.opinet.co.kr/searRgSelect.do')
In [13]:
# 종로구라는 글자가 보이는 리스트 박스의 XPath를 이용하여 element를 찾고 gu_list_raw 변수에 저장한다.
gu_list_raw = driver.find_element_by_xpath('''//*[@id="SIGUNGU_NM0"]''')
In [14]:
# 구 리스트는 find_elements_by_tag_name으로 option이라는 태그를 찾으면 된다.
gu_list = gu_list_raw.find_elements_by_tag_name('option')
In [15]:
gu_names = [option.get_attribute('value') for option in gu_list]
gu_names.remove('')
gu_names
Out[15]:
['가평군',
 '고양시',
 '고양시덕양구',
 '고양시일산동구',
 '고양시일산서구',
 '과천시',
 '광명시',
 '광주시',
 '구리시',
 '군포시',
 '김포시',
 '남양주시',
 '동두천시',
 '부천시',
 '성남시',
 '성남시분당구',
 '성남시수정구',
 '성남시중원구',
 '수원시',
 '수원시권선구',
 '수원시영통구',
 '수원시장안구',
 '수원시팔달구',
 '시흥시',
 '안산시',
 '안산시단원구',
 '안산시상록구',
 '안성시',
 '안양시',
 '안양시동안구',
 '안양시만안구',
 '양주시',
 '양평군',
 '여주시',
 '연천군',
 '오산시',
 '용인시',
 '용인시기흥구',
 '용인시수지구',
 '용인시처인구',
 '의왕시',
 '의정부시',
 '이천시',
 '파주시',
 '평택시',
 '포천시',
 '하남시',
 '화성시']
In [16]:
#gu_names에서 첫번째 것을 한번 시험 삼아 입력해보자.
element = driver.find_element_by_id('SIGUNGU_NM0')
element.send_keys(gu_names[0])
In [17]:
#조회버튼의 Xpath를 찾아서 클릭
xpath ='''//*[@id="searRgSelect"]/span'''
element_sel_gu = driver.find_element_by_xpath(xpath).click()
In [18]:
# #엑셀 저장 버튼을 눌러서 엑셀 내용으로 저장하자.
# xpath = '''//*[@id="glopopd_excel"]/span'''
# element_get_excel = driver.find_element_by_xpath(xpath).click()
In [19]:
# import time
# from tqdm import tqdm_notebook

# for gu in tqdm_notebook(gu_names):
#     element = driver.find_element_by_id('SIGUNGU_NM0')
#     element.send_keys(gu)
    
#     time.sleep(2)
    
#     xpath ='''//*[@id="searRgSelect"]/span'''
#     element_sel_gu = driver.find_element_by_xpath(xpath).click()
    
#     time.sleep(1)
    
#     xpath = '''//*[@id="glopopd_excel"]/span'''
#     element_get_excel = driver.find_element_by_xpath(xpath).click()
    
#     time.sleep(1)
In [20]:
# 드라이버를 닫고, 이제 저장된 엑셀 파일로 셀프 주유소가 실제로 저렴한지 여부를 확인해 보자.
driver.close()
 
  • 구별 주유 가격에 대한 데이터의 정리 다운받은 25개의 엑셀 파일을 이전처럼 read를 이용하여 읽으면 25줄을 입력해야지만 모두 읽을 수 있지만 파이썬의 glob 모듈을 사용하면 손쉽게 열 수 있다.
In [21]:
from glob import glob
In [22]:
# 지역*.xls는 지역으로 시작하면서 확장자가 xls인 파일들
glob('./주유소위치폴더/지역*.xls')
Out[22]:
['./주유소위치폴더\\지역_위치별(주유소) (1).xls',
 './주유소위치폴더\\지역_위치별(주유소) (10).xls',
 './주유소위치폴더\\지역_위치별(주유소) (11).xls',
 './주유소위치폴더\\지역_위치별(주유소) (12).xls',
 './주유소위치폴더\\지역_위치별(주유소) (13).xls',
 './주유소위치폴더\\지역_위치별(주유소) (14).xls',
 './주유소위치폴더\\지역_위치별(주유소) (15).xls',
 './주유소위치폴더\\지역_위치별(주유소) (16).xls',
 './주유소위치폴더\\지역_위치별(주유소) (17).xls',
 './주유소위치폴더\\지역_위치별(주유소) (18).xls',
 './주유소위치폴더\\지역_위치별(주유소) (19).xls',
 './주유소위치폴더\\지역_위치별(주유소) (2).xls',
 './주유소위치폴더\\지역_위치별(주유소) (20).xls',
 './주유소위치폴더\\지역_위치별(주유소) (21).xls',
 './주유소위치폴더\\지역_위치별(주유소) (22).xls',
 './주유소위치폴더\\지역_위치별(주유소) (23).xls',
 './주유소위치폴더\\지역_위치별(주유소) (24).xls',
 './주유소위치폴더\\지역_위치별(주유소) (25).xls',
 './주유소위치폴더\\지역_위치별(주유소) (3).xls',
 './주유소위치폴더\\지역_위치별(주유소) (4).xls',
 './주유소위치폴더\\지역_위치별(주유소) (5).xls',
 './주유소위치폴더\\지역_위치별(주유소) (6).xls',
 './주유소위치폴더\\지역_위치별(주유소) (7).xls',
 './주유소위치폴더\\지역_위치별(주유소) (8).xls',
 './주유소위치폴더\\지역_위치별(주유소) (9).xls',
 './주유소위치폴더\\지역_위치별(주유소).xls']
In [23]:
# station_files 변수에 각 엑셀 파일의 경로와 이름을 리스트로 저장
stations_files = glob('./주유소위치폴더/지역*.xls')
stations_files
Out[23]:
['./주유소위치폴더\\지역_위치별(주유소) (1).xls',
 './주유소위치폴더\\지역_위치별(주유소) (10).xls',
 './주유소위치폴더\\지역_위치별(주유소) (11).xls',
 './주유소위치폴더\\지역_위치별(주유소) (12).xls',
 './주유소위치폴더\\지역_위치별(주유소) (13).xls',
 './주유소위치폴더\\지역_위치별(주유소) (14).xls',
 './주유소위치폴더\\지역_위치별(주유소) (15).xls',
 './주유소위치폴더\\지역_위치별(주유소) (16).xls',
 './주유소위치폴더\\지역_위치별(주유소) (17).xls',
 './주유소위치폴더\\지역_위치별(주유소) (18).xls',
 './주유소위치폴더\\지역_위치별(주유소) (19).xls',
 './주유소위치폴더\\지역_위치별(주유소) (2).xls',
 './주유소위치폴더\\지역_위치별(주유소) (20).xls',
 './주유소위치폴더\\지역_위치별(주유소) (21).xls',
 './주유소위치폴더\\지역_위치별(주유소) (22).xls',
 './주유소위치폴더\\지역_위치별(주유소) (23).xls',
 './주유소위치폴더\\지역_위치별(주유소) (24).xls',
 './주유소위치폴더\\지역_위치별(주유소) (25).xls',
 './주유소위치폴더\\지역_위치별(주유소) (3).xls',
 './주유소위치폴더\\지역_위치별(주유소) (4).xls',
 './주유소위치폴더\\지역_위치별(주유소) (5).xls',
 './주유소위치폴더\\지역_위치별(주유소) (6).xls',
 './주유소위치폴더\\지역_위치별(주유소) (7).xls',
 './주유소위치폴더\\지역_위치별(주유소) (8).xls',
 './주유소위치폴더\\지역_위치별(주유소) (9).xls',
 './주유소위치폴더\\지역_위치별(주유소).xls']
 

2. 데이터 결합¶

In [24]:
# 상반기와 하반기 데이터를 상하로 결합
df=pd.concat([first,second],ignore_index=True)
df
Out[24]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190101 SK에너지 셀프 1673 1465 1365 0
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190102 SK에너지 셀프 1673 1465 1365 0
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190103 SK에너지 셀프 1673 1465 1365 0
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190104 SK에너지 셀프 1673 1465 1365 0
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190105 SK에너지 셀프 1673 1465 1365 0
... ... ... ... ... ... ... ... ... ... ... ...
181709 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191227 GS칼텍스 셀프 0 1540 1389 1100
181710 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191228 GS칼텍스 셀프 0 1540 1389 1100
181711 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191229 GS칼텍스 셀프 0 1540 1389 1100
181712 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191230 GS칼텍스 셀프 0 1540 1389 1100
181713 A0032659 서울 중랑구 지에스칼텍스㈜ 소망주유소 서울 중랑구 망우로 475 20191231 GS칼텍스 셀프 0 1540 1389 1100

181714 rows × 11 columns

 

[웹 데이터 크롤링]

In [25]:
# concat 명령으로 폴더내의 자료들을 합친다.
tmp_raw = []

for file_name in stations_files:
    tmp = pd.read_excel(file_name, header=2)
    tmp_raw.append(tmp)
    
station_raw = pd.concat(tmp_raw)
In [26]:
station_raw.info()
 
<class 'pandas.core.frame.DataFrame'>
Int64Index: 537 entries, 0 to 39
Data columns (total 10 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   지역      537 non-null    object
 1   상호      537 non-null    object
 2   주소      537 non-null    object
 3   상표      537 non-null    object
 4   전화번호    537 non-null    object
 5   셀프여부    537 non-null    object
 6   고급휘발유   537 non-null    object
 7   휘발유     537 non-null    object
 8   경유      537 non-null    object
 9   실내등유    537 non-null    object
dtypes: object(10)
memory usage: 46.1+ KB
In [27]:
station_raw.head()
Out[27]:
  지역 상호 주소 상표 전화번호 셀프여부 고급휘발유 휘발유 경유 실내등유
0 서울특별시 (주)보성 세곡주유소 서울 강남구 헌릉로 731 (세곡동) SK에너지 02-445-6870 Y - 1435 1235 -
1 서울특별시 GS칼텍스(주)직영 산성주유소 서울 강남구 헌릉로 730 GS칼텍스 02-2226-4963 Y - 1445 1245 -
2 서울특별시 방죽주유소 서울 강남구 밤고개로 215 (율현동) GS칼텍스 02-459-3434 Y - 1459 1259 -
3 서울특별시 오일프러스 셀프 서울 강남구 남부순환로 2651 (도곡동) SK에너지 02-3462-5100 Y 1612 1463 1259 -
4 서울특별시 현대오일뱅크 도곡셀프주유소 서울 강남구 남부순환로 2718 (도곡2동) 현대오일뱅크 02-529-5101 Y 1627 1473 1269 -
 

3. 데이터 가공 및 분석¶

In [28]:
# 데이터 프레임 정보 조회
df.info()
 
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 181714 entries, 0 to 181713
Data columns (total 11 columns):
 #   Column  Non-Null Count   Dtype 
---  ------  --------------   ----- 
 0   번호      181714 non-null  object
 1   지역      181714 non-null  object
 2   상호      181714 non-null  object
 3   주소      181714 non-null  object
 4   기간      181714 non-null  int64 
 5   상표      181714 non-null  object
 6   셀프여부    181714 non-null  object
 7   고급휘발유   181714 non-null  int64 
 8   휘발유     181714 non-null  int64 
 9   경유      181714 non-null  int64 
 10  실내등유    181714 non-null  int64 
dtypes: int64(5), object(6)
memory usage: 15.3+ MB
In [29]:
# 상표 컬럼의 고유값 조회
df['상표'].unique()
Out[29]:
array(['SK에너지', 'GS칼텍스', 'S-OIL', '현대오일뱅크', '알뜰주유소', 'NH-OIL', '알뜰(ex)',
       '자가상표'], dtype=object)
In [30]:
# 지역 컬럼의 고유값 조회
df['지역'].unique()
Out[30]:
array(['서울 강남구', '서울 강동구', '서울 강북구', '서울 강서구', '서울 관악구', '서울 광진구',
       '서울 구로구', '서울 금천구', '서울 노원구', '서울 도봉구', '서울 동대문구', '서울 동작구',
       '서울 마포구', '서울 서대문구', '서울 서초구', '서울 성동구', '서울 성북구', '서울 송파구',
       '서울 양천구', '서울 영등포구', '서울 용산구', '서울 은평구', '서울 종로구', '서울 중구',
       '서울 중랑구'], dtype=object)
In [31]:
# 지역 컬럼의 값을 이용하여 시와 구를 분리
df['시'] = df['지역'].str.split(' ').str[0]
df['구'] = df['지역'].str.split(' ').str[1]
df.head()
Out[31]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유 시 구
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190101 SK에너지 셀프 1673 1465 1365 0 서울 강남구
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190102 SK에너지 셀프 1673 1465 1365 0 서울 강남구
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190103 SK에너지 셀프 1673 1465 1365 0 서울 강남구
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190104 SK에너지 셀프 1673 1465 1365 0 서울 강남구
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190105 SK에너지 셀프 1673 1465 1365 0 서울 강남구
In [32]:
# 시 컬럼의 서울을 서울특별시로 변환
df['시'] = '서울특별시'
df.head()
Out[32]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유 시 구
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190101 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190102 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190103 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190104 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 20190105 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
In [33]:
# 기간 컬럼을 datetime 형식으로 변경
df['기간'] = pd.to_datetime(df['기간'],format='%Y%m%d')
df.head()
Out[33]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유 시 구
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-01 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-02 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-03 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-04 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-05 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구
In [34]:
# 기간 컬럼에서 dt 타입을 이용하여 년, 월, 일, 요일 컬럼 생성
df['년'] = df['기간'].dt.year
df['월'] = df['기간'].dt.month
df['일'] = df['기간'].dt.day
df['요일'] = df['기간'].dt.day_name()
df.head()
Out[34]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유 시 구 년 월 일 요일
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-01 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 1 Tuesday
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-02 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 2 Wednesday
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-03 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 3 Thursday
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-04 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 4 Friday
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-05 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 5 Saturday
In [35]:
df[df['상호'] == '(주)동하힐탑셀프주유소']
Out[35]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유 시 구 년 월 일 요일
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-01 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 1 Tuesday
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-02 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 2 Wednesday
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-03 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 3 Thursday
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-04 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 4 Friday
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-05 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 5 Saturday
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
90769 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-12-27 SK에너지 셀프 1802 1657 1495 0 서울특별시 강남구 2019 12 27 Friday
90770 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-12-28 SK에너지 셀프 1802 1657 1495 0 서울특별시 강남구 2019 12 28 Saturday
90771 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-12-29 SK에너지 셀프 1802 1657 1495 0 서울특별시 강남구 2019 12 29 Sunday
90772 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-12-30 SK에너지 셀프 1802 1657 1495 0 서울특별시 강남구 2019 12 30 Monday
90773 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-12-31 SK에너지 셀프 1802 1657 1495 0 서울특별시 강남구 2019 12 31 Tuesday

365 rows × 17 columns

In [36]:
# 휘발유 가격이 비싼 5개 판매가격을 출력하세요.
df['휘발유'].sort_values(ascending=False).head()
Out[36]:
177210    2356
177216    2356
177217    2356
177211    2356
177212    2356
Name: 휘발유, dtype: int64
In [37]:
# 휘발유 가격이 저렴한 5개 판매가격을 출력하세요. (단 휘발유가 0원인 데이터는 제외)
df[df['휘발유'] != 0]['휘발유'].sort_values().head(5)
Out[37]:
13148    1255
82475    1260
82474    1260
82476    1260
82998    1265
Name: 휘발유, dtype: int64
In [38]:
df1 = df.copy()
In [39]:
df.head()
Out[39]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유 시 구 년 월 일 요일
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-01 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 1 Tuesday
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-02 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 2 Wednesday
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-03 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 3 Thursday
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-04 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 4 Friday
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-05 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 5 Saturday
 

[웹 데이터 크롤링]

In [40]:
# 원하는 컬럼만 가지고 오고 이름도 다시 정의해서 stations 변수에 저장
# 이 단원에서는 휘발유만 다루겠다.
stations = pd.DataFrame({'Oil_store': station_raw['상호'],
                         '주소': station_raw['주소'],
                         '가격': station_raw['휘발유'],
                         '셀프': station_raw['셀프여부'],
                         '상표': station_raw['상표']
                        })
stations.head()
Out[40]:
  Oil_store 주소 가격 셀프 상표
0 (주)보성 세곡주유소 서울 강남구 헌릉로 731 (세곡동) 1435 Y SK에너지
1 GS칼텍스(주)직영 산성주유소 서울 강남구 헌릉로 730 1445 Y GS칼텍스
2 방죽주유소 서울 강남구 밤고개로 215 (율현동) 1459 Y GS칼텍스
3 오일프러스 셀프 서울 강남구 남부순환로 2651 (도곡동) 1463 Y SK에너지
4 현대오일뱅크 도곡셀프주유소 서울 강남구 남부순환로 2718 (도곡2동) 1473 Y 현대오일뱅크
In [41]:
# 추가로 주소에서 구 이름만 추출하자.
# 빈칸을 기준으로 분리 후 2번째 단어 선택
stations['구'] = [eachAddress.split()[1] for eachAddress in stations['주소']]
stations.head()
Out[41]:
  Oil_store 주소 가격 셀프 상표 구
0 (주)보성 세곡주유소 서울 강남구 헌릉로 731 (세곡동) 1435 Y SK에너지 강남구
1 GS칼텍스(주)직영 산성주유소 서울 강남구 헌릉로 730 1445 Y GS칼텍스 강남구
2 방죽주유소 서울 강남구 밤고개로 215 (율현동) 1459 Y GS칼텍스 강남구
3 오일프러스 셀프 서울 강남구 남부순환로 2651 (도곡동) 1463 Y SK에너지 강남구
4 현대오일뱅크 도곡셀프주유소 서울 강남구 남부순환로 2718 (도곡2동) 1473 Y 현대오일뱅크 강남구
In [42]:
# head만 보기에는 문제 없으나 500여개의 데이터가 어떤 상태인지 확인하기 쉽지 않다.
# unique() 검사 수행
stations['구'].unique()
Out[42]:
array(['강남구', '도봉구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구',
       '송파구', '양천구', '강동구', '영등포구', '용산구', '은평구', '종로구', '중구', '중랑구',
       '강북구', '강서구', '관악구', '광진구', '구로구', '금천구', '노원구'], dtype=object)
In [43]:
# 가격 컬럼의 unique를 구해보자.
stations['가격'].unique()
Out[43]:
array(['1435', '1445', '1459', '1463', '1473', '1479', '1485', '1488',
       '1495', '1505', '1518', '1525', '1527', '1528', '1533', '1538',
       '1548', '1550', '1626', '1628', '1697', '1788', '1847', '1856',
       '1918', '1935', '1938', '1953', '1971', '1988', '2075', '-', 1412,
       1414, 1419, 1425, 1435, 1438, 1448, 1449, 1453, 1475, 1479, 1498,
       '1425', '1437', '1439', '1442', '1458', '1465', '1468', '1469',
       '1477', '1497', '1498', '1499', '1599', '1610', 1459, 1489, 1509,
       1565, '1457', '1466', '1467', '1557', '1689', '1757', 1434, 1439,
       1444, 1455, 1462, 1464, 1467, 1474, 1478, '1379', '1449', '1464',
       '1475', '1508', '1539', '1571', '1595', '1662', '1867', '1888',
       '1942', 1427, 1458, 1465, 1516, 1518, 1545, 1635, 1705, 1748, 1755,
       1398, 1428, 1437, 1442, 1457, 1499, 1440, 1443, 1454, 1463, 1468,
       1473, 1477, 1487, 1497, 1638, 1698, 1399, 1408, 1418, 1423, 1460,
       1480, 1519, 1694, 1697, 1476, 1488, 1494, 1495, 1505, 1515, 1547,
       1548, 1578, 1963, '1384', '1389', '1399', '1409', '1415', '1417',
       '1429', '1438', '1440', '1448', '1455', '1494', '1748', '1868',
       '1940', '1950', '1954', '1979', 1568, 1718, 1720, 1764, 1829, 1849,
       1858, 1863, 1898, 1990, 1998, 2189, '1424', '1428', '1434', '1447',
       '1478', 1510, 1517, 1540, 1888, 1948, 1995, '1648', '1758', '1828',
       '1848', '1869', '1985', '2099', '2129', '2270', 1445, 1447, 1456,
       1508, 1409, 1429, 1387, 1395, 1405, 1416, 1491, 1528, 1538, 1615,
       1758, '1487', '1489', 1469, 1529, 1537, 1485, 1496, 1942, '1530',
       '1598'], dtype=object)
 

휘발유나 경유가 0인 데이터는 전체 데이터 대비 비율이 적기때문에 바로 삭제처리

In [44]:
drop_df = df1[(df1['휘발유'] == 0)  | (df1['경유'] ==0)]
print(f'기존데이터: {df1.shape[0]}개\n휘발유나 경유가 0인 데이터: {drop_df.shape[0]}개')
df1 = df1.drop(index=drop_df.index)
print(f'삭제 후 데이터: {df1.shape[0]}개')
 
기존데이터: 181714개
휘발유나 경유가 0인 데이터: 68개
삭제 후 데이터: 181646개
 

도로명 column 추가

In [45]:
df1['도로명'] = df1['주소'].str.split(' ').str[2]
df1.head()
Out[45]:
  번호 지역 상호 주소 기간 상표 셀프여부 고급휘발유 휘발유 경유 실내등유 시 구 년 월 일 요일 도로명
0 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-01 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 1 Tuesday 논현로
1 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-02 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 2 Wednesday 논현로
2 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-03 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 3 Thursday 논현로
3 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-04 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 4 Friday 논현로
4 A0006039 서울 강남구 (주)동하힐탑셀프주유소 서울 강남구 논현로 640 2019-01-05 SK에너지 셀프 1673 1465 1365 0 서울특별시 강남구 2019 1 5 Saturday 논현로
 

[웹 데이터 크롤링]

In [46]:
# 숫자가 아닌 '-' 문자가 끼어있다.
stations[stations['가격']=='-']
Out[46]:
  Oil_store 주소 가격 셀프 상표 구
38 삼성주유소 서울 강남구 삼성로 521 (삼성동) - N SK에너지 강남구
39 동우주유소 서울특별시 강남구 봉은사로 311 (논현동) - N SK에너지 강남구
21 신성주유소 서울 동대문구 장한로 29 (장안동) - N 현대오일뱅크 동대문구
12 서강주유소 서울 마포구 독막로 134 (창전동) - N SK에너지 마포구
35 전당앞주유소 서울 서초구 남부순환로 2391 (서초동) - N SK에너지 서초구
30 성락주유소 서울 영등포구 가마산로 414 (신길동) - Y S-OIL 영등포구
16 삼융주유소 서울 은평구 수색로 299 (수색동) - N SK에너지 은평구
10 에스씨(주) 역전주유소 서울 중구 퇴계로 15 - N GS칼텍스 중구
15 CJ대한통운㈜ 신림주유소 서울 관악구 남부순환로 1515 (신림동) - N GS칼텍스 관악구
14 양지진흥개발(주)상계주유소 서울 노원구 노해로 527 (상계동) - N SK에너지 노원구
38 삼성주유소 서울 강남구 삼성로 521 (삼성동) - N SK에너지 강남구
39 동우주유소 서울특별시 강남구 봉은사로 311 (논현동) - N SK에너지 강남구
In [47]:
# '-' 문자가 포함된 데이터 제외시켜 버리기
stations = stations[stations['가격'] != '-']
stations.head()
Out[47]:
  Oil_store 주소 가격 셀프 상표 구
0 (주)보성 세곡주유소 서울 강남구 헌릉로 731 (세곡동) 1435 Y SK에너지 강남구
1 GS칼텍스(주)직영 산성주유소 서울 강남구 헌릉로 730 1445 Y GS칼텍스 강남구
2 방죽주유소 서울 강남구 밤고개로 215 (율현동) 1459 Y GS칼텍스 강남구
3 오일프러스 셀프 서울 강남구 남부순환로 2651 (도곡동) 1463 Y SK에너지 강남구
4 현대오일뱅크 도곡셀프주유소 서울 강남구 남부순환로 2718 (도곡2동) 1473 Y 현대오일뱅크 강남구
In [48]:
# 가격을 float 형으로 변환한다.
stations['가격'] = [float(value) for value in stations['가격']]
In [49]:
# 25개의 엑셀을 합쳤기 때문에 index가 중복될 수 있다.
# reset_index 명령으로 인덱스를 처음부터 다시 기록하자.
# index라는 컬럼이 하나 더 생기는데 그 부분을 제거한다.
stations.reset_index(inplace=True)
del stations['index']
In [50]:
stations.info()
 
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 525 entries, 0 to 524
Data columns (total 6 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   Oil_store  525 non-null    object 
 1   주소         525 non-null    object 
 2   가격         525 non-null    float64
 3   셀프         525 non-null    object 
 4   상표         525 non-null    object 
 5   구          525 non-null    object 
dtypes: float64(1), object(5)
memory usage: 24.7+ KB
 

Visualization¶

 

1) 기름 유형의 기간에 따른 가격 변동¶

  • 목적: 기름 유형별 가격 변화 파악
  • 시각화 채택 이유: 1년간 기름유형별 가격 등락 파악에 용이
In [51]:
plt.figure(figsize=(12,7))
aaa = df.pivot_table(values=['휘발유', '경유','고급휘발유','실내등유'], index='기간')
sns.lineplot(data=aaa, palette="tab10", linewidth=2.5)
plt.show()
 
 

2) 상표 및 셀프여부에 따른 분포 정리¶

  • 목적: 서울시 내 각 정유사 및 주유유형별 분포를 확인하여 가장 대중화된 업체 및 주유형태 파악
  • 시각화 채택 이유: 상표와 셀프여부에 따른 비중 파악에 용이하며, 자료 겹칩을 방지하기 위해 explode를 사용하여 가시성 증대
In [52]:
fig, ax = plt.subplots(1, 2, figsize=(15,7), dpi=300)

count_df = df1[['상호','상표','셀프여부']].drop_duplicates()

size = count_df['상표'].value_counts()
labels = count_df['상표'].value_counts().index
colors=plt.cm.Wistia(np.linspace(0,1,count_df['상표'].value_counts().shape[0]))
explode = [0,0,0,0,0,0.1,0.2,0.3]

ax[0].pie(size, labels=labels, colors=colors, shadow=True, startangle=45, explode=explode,autopct='%.1f%%')
ax[0].set_title('상표별 데이터 분포',fontsize=20)
ax[0].legend()


size = count_df['셀프여부'].value_counts()
labels = count_df['셀프여부'].value_counts().index
colors = plt.cm.Pastel1(np.linspace(0, 1, 2))
explode = [0.01, 0.01]

ax[1].pie(size, labels=labels, colors=colors, shadow=True, startangle=45, explode=explode,autopct='%.1f%%')
ax[1].set_title('셀프여부 데이터 분포',fontsize=20)
ax[1].legend()

plt.tight_layout()
plt.show()
 
 

3) 휘발유와 경유 가격의 분포¶

  • 목적 : 1년간 유가가 어느 가격대에 많이 분포되어 있는지 확인

  • 시각화 채택 이유 : 서울시의 1년간의 유가 정보를 통해 앞으로의 값에 대한 가능성의 정도를 추정하기에 용이

  • 시각화를 통해 얻은 정보 : 휘발유와 경유의 분포 모양이 비슷하며 가우시안 분포를 어느정도 따라간다.

      휘발유는 1400~1600원 경유는 1300~1500원 정도를 유지하였다.
In [53]:
fig, ax = plt.subplots(2, 1, figsize=(15,7), dpi=300)

sns.distplot(df1['휘발유'], color = 'pink',ax=ax[0])
ax[0].set_title('휘발유', fontsize = 20)
ax[0].set_xlabel('가격', fontsize = 0)
ax[0].set_ylabel('density', fontsize = 16)

sns.distplot(df1['경유'], color = 'pink',ax=ax[1])
ax[1].set_title('경유', fontsize = 20)
ax[1].set_xlabel('가격', fontsize = 16)
ax[1].set_ylabel('density', fontsize = 16)

plt.tight_layout()
plt.show()
 
 

4) 월간 고급휘발유와 실내등유의 평균 가격¶

  • 목적: 시간의 흐름에 따른 고급휘발유와 실내등유의 가격 변동 파악
  • 시각화 채택 이유: 어떤 시즌에 유가의 변동이 일어나는지 파악하는데 용이 - 시각화를 통해 얻은 정보 : 고급휘발유의 가격은 S/S 시즌에 높아지고 F/W 시즌에 낮아진다. 반면에 실내등유는 여름철에 가격이 낮아지고 겨울철에 높아진다. -> 수요가 많아지면서 가격또한 높아짐을 알 수 있다.
In [ ]:
 
In [54]:
# 월별 고급휘발유, 실내등유 평균가격
data = df1[(df['고급휘발유'] != 0) | (df['실내등유'] !=0)]
data = data.pivot_table(index='월',values=['고급휘발유','실내등유'],aggfunc='mean').reset_index()

fig, ax = plt.subplots(1, 2, figsize=(10,5), dpi=300)

sns.barplot(x = data['월'], y = data['고급휘발유'], palette = 'pastel',ax=ax[0])
ax[0].set_xlabel(xlabel = '월', fontsize = 12)
ax[0].set_ylabel(ylabel = '평균가격', fontsize = 12)
ax[0].set_title(label = '고급휘발유', fontsize = 15)

sns.barplot(x = data['월'], y = data['실내등유'], palette = 'pastel',ax=ax[1])
ax[1].set_xlabel(xlabel = '월', fontsize = 12)
ax[1].set_ylabel(ylabel = '평균가격', fontsize = 12)
ax[1].set_title(label = '실내등유', fontsize = 15)

plt.tight_layout()
plt.show()
 
 

5) 1년간 각 지역별 휘발유와 경유의 평균 가격¶

  • 목적: 어느 지역이 가격대가 높게 형성되었고 또 어떤 지역이 가격대가 낮은지 확인

  • 시각화 채택 이유: 가격대별로 지역들을 정렬하여 한눈에 각 지역의 유가를 파악하는데에 용이

In [55]:
fig, ax = plt.subplots(2, 1, figsize=(15,7), dpi=300, sharex=True)

data = df1.pivot_table(index='구',values=['휘발유','경유']).sort_values(by='휘발유',ascending=False).reset_index()
data
Out[55]:
  구 경유 휘발유
0 중구 1835.953011 1976.948052
1 용산구 1755.083886 1887.254609
2 종로구 1696.179182 1838.554945
3 강남구 1577.271600 1713.328332
4 마포구 1555.951075 1690.628367
5 성동구 1446.205749 1583.659561
6 서초구 1456.007124 1577.705147
7 강동구 1442.151680 1577.487784
8 영등포구 1442.211041 1568.256215
9 노원구 1402.993780 1531.221551
10 서대문구 1392.777321 1528.925416
11 구로구 1381.307984 1525.413845
12 송파구 1394.908481 1518.951058
13 관악구 1395.139450 1518.635029
14 동작구 1392.197748 1513.717111
15 양천구 1379.040346 1513.057730
16 성북구 1376.006440 1508.315789
17 동대문구 1366.267426 1507.402202
18 도봉구 1370.153055 1494.393741
19 광진구 1357.036899 1490.597054
20 강서구 1357.277517 1489.164266
21 은평구 1351.908372 1481.964026
22 금천구 1347.144787 1477.121992
23 중랑구 1331.235747 1468.997552
24 강북구 1328.962611 1454.131601
 
In [77]:
sns.barplot(x = data['구'], y = data['휘발유'], palette = 'husl',ax=ax[0])
ax[0].set_xlabel(xlabel = '사라져', fontsize = 0)
ax[0].set_ylabel(ylabel = '평균가격', fontsize = 15)
ax[0].set_title(label = '휘발유', fontsize = 15)

sns.barplot(x = data['구'], y = data['경유'], palette = 'husl',ax=ax[1])
ax[1].set_xlabel(xlabel = '지역', fontsize = 15)
ax[1].set_ylabel(ylabel = '평균가격', fontsize = 15)
ax[1].set_title(label = '경유', fontsize = 15)

plt.tight_layout()
plt.show()
 
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
C:\anaconda3\lib\site-packages\pandas\core\indexes\base.py in get_loc(self, key, method, tolerance)
   3079             try:
-> 3080                 return self._engine.get_loc(casted_key)
   3081             except KeyError as err:

pandas\_libs\index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas\_libs\index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas\_libs\hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

pandas\_libs\hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: '휘발유'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
<ipython-input-77-96bb90c02cbe> in <module>
----> 1 sns.barplot(x = data['구'], y = data['휘발유'], palette = 'husl',ax=ax[0])
      2 ax[0].set_xlabel(xlabel = '사라져', fontsize = 0)
      3 ax[0].set_ylabel(ylabel = '평균가격', fontsize = 15)
      4 ax[0].set_title(label = '휘발유', fontsize = 15)
      5 

C:\anaconda3\lib\site-packages\pandas\core\frame.py in __getitem__(self, key)
   3022             if self.columns.nlevels > 1:
   3023                 return self._getitem_multilevel(key)
-> 3024             indexer = self.columns.get_loc(key)
   3025             if is_integer(indexer):
   3026                 indexer = [indexer]

C:\anaconda3\lib\site-packages\pandas\core\indexes\base.py in get_loc(self, key, method, tolerance)
   3080                 return self._engine.get_loc(casted_key)
   3081             except KeyError as err:
-> 3082                 raise KeyError(key) from err
   3083 
   3084         if tolerance is not None:

KeyError: '휘발유'
 

6) 지역의 상표별 주유소 개수¶

  • 목적: 지역별로 진출한 정유사 파악
  • 시각화 채택 이유: 서울시 모든 지역별로 눈에 띄게 시장을 장악한 정유업체 파악에 용이
In [57]:
data = df1[['구','상호','상표','셀프여부']].drop_duplicates()
data
Out[57]:
  구 상호 상표 셀프여부
0 강남구 (주)동하힐탑셀프주유소 SK에너지 셀프
181 강남구 (주)만정에너지 GS칼텍스 일반
362 강남구 (주)보성 세곡주유소 SK에너지 셀프
543 강남구 (주)선진도곡주유소 S-OIL 일반
724 강남구 (주)소모에너지엔테크놀러지 쎈트럴주유소 GS칼텍스 일반
... ... ... ... ...
157241 송파구 위례제일주유소 S-OIL 셀프
158498 송파구 현대오일뱅크㈜직영 잠실셀프주유소 현대오일뱅크 셀프
160206 양천구 가로공원주유소 SK에너지 일반
162586 양천구 현대오일뱅크(주)직영 양천셀프주유소 현대오일뱅크 셀프
173235 은평구 번개셀프주유소 자가상표 셀프

528 rows × 4 columns

In [58]:
data = df1[['구','상호','상표','셀프여부']].drop_duplicates()

plt.figure(figsize = (25,8))
ax = sns.countplot(x = '구', data = data, palette = 'pastel',hue='상표')
ax.set_title(label = '각 지역의 상표별 주유소 개수', fontsize = 20)
ax.set_xlabel(xlabel = '지역', fontsize = 16)
ax.set_ylabel(ylabel = '개수', fontsize = 16)
plt.show()
 
 

7) 상표별 휘발유 분포¶

  • 목적: 주유형태에 따른 상표별 점유율 파악
  • 시각화 채택 이유: 많은 데이터의 분포와 범위를 한눈에 보기 쉽게 나타냄
In [59]:
fig, ax = plt.subplots(2, 1, figsize=(20,8), dpi=300)

sns.violinplot(data=df1, x="상표", y="휘발유", hue="셀프여부",
               split=True, inner="quart", linewidth=1, palette="Set2",ax=ax[0]
               )
ax[0].set_title('휘발유', fontsize=25)
ax[0].set_ylabel('분포',fontsize=20)
ax[0].set_xlabel('상표',fontsize=0)

sns.violinplot(data=df1, x="상표", y="경유", hue="셀프여부",
               split=True, inner="quart", linewidth=1, palette="Set2",ax=ax[1]
               )
ax[1].set_title('경유', fontsize=25)
ax[1].set_ylabel('분포',fontsize=20)
ax[1].set_xlabel('상표',fontsize=20)

plt.tight_layout()
plt.show()
 
 

8) 서울특별시 전체(일반/셀프) 주유소 분포¶

  • 목적: 지역별 가장 많은 주유소를 갖고 있는 지역 파악
  • 시각화 채택 이유: 주유소 갯수 데이터 크기를 내림차순으로 비교 용이
In [60]:
a = df1[['구','상호']].drop_duplicates()
a
Out[60]:
  구 상호
0 강남구 (주)동하힐탑셀프주유소
181 강남구 (주)만정에너지
362 강남구 (주)보성 세곡주유소
543 강남구 (주)선진도곡주유소
724 강남구 (주)소모에너지엔테크놀러지 쎈트럴주유소
... ... ...
89866 중랑구 재원에너지㈜ 범아주유소
90047 중랑구 중랑교
90228 중랑구 지에스칼텍스㈜ 대원주유소
90409 중랑구 지에스칼텍스㈜ 소망주유소
157241 송파구 위례제일주유소

512 rows × 2 columns

In [61]:
unique = a['구'].value_counts()

plt.figure(figsize=(10,8))
plt.title('지역별 ')
sns.barplot(y=unique.index,x=unique.values, palette="deep")
Out[61]:
<AxesSubplot:title={'center':'지역별 '}>
 
 

9) 각 지역의 셀프 여부별 주유소 개수¶

  • 목적: 지역별 셀프여부에 따른 시장점유율 파악
  • 시각화 채택 이유: 서울시 내 모든 지역에서 가시성 높은 셀프/일반 단 2개의 데이터 비교 용이
In [62]:
data = df1[['구','상호','상표','셀프여부']].drop_duplicates()

plt.figure(figsize = (25,8))
ax = sns.countplot(x = '구', data = data, palette = 'hls',hue='셀프여부')
ax.set_title(label = '각 지역의 셀프여부별 주유소 개수', fontsize = 20)
ax.set_xlabel(xlabel = '지역', fontsize = 16)
ax.set_ylabel(ylabel = '개수', fontsize = 16)
plt.show()
 
 

10) 월별 셀프여부에 따른 휘발유 분포¶

  • 목적: 월별 주유 유형의 점유율 파악
  • 시각화 채택 이유: 많은 데이터의 분포와 범위를 한눈에 보기 쉽게 나타냄
In [63]:
fig, ax = plt.subplots(2, 1, figsize=(15,7), dpi=300, sharex=True)


sns.violinplot(data=df1, x="월", y="휘발유", hue="셀프여부",
               split=True, inner="quart", linewidth=1, palette="Set3",ax=ax[0]
               ) #특정 순서를 암시하지 않는, 그냥 고유한 색상이라고 볼 수 있기 때문니다.
ax[0].set_title('휘발유', fontsize=20)
ax[0].set_xlabel('월',fontsize=0)
ax[0].set_ylabel('분포',fontsize=15)

sns.violinplot(data=df1, x="월", y="경유", hue="셀프여부",
               split=True, inner="quart", linewidth=1, palette="Set3",ax=ax[1]
               )
ax[1].set_title('경유', fontsize=20)
ax[1].set_xlabel('월',fontsize=15)
ax[1].set_ylabel('분포',fontsize=15)

plt.tight_layout()
plt.show()
 
 

11) 셀프 주유소와 일반 주유소의 가격 분포¶

  • 목적: 전반적인 데이터들의 결과를 보고 셀프, 일발 주유소의 차이점을 이해할수 있다.

  • 시각화 채택 이유: boxplot으로 간편하게 셀프 컬럼을 기준으로 가격 분포를 확인할 수 있게 되었다. 코드의 결과를 보면 직사각형이 데이터의 대다수가 몰려 있는 곳이라는 것은 직관적으로 알 수 있다. 전반적으로 셀프 주유소인 경유가 가격이 낮게 되어 있다.

 
  • 시각화 채택 이유: Swarmplot을 같이 그려보면 좀 더 확실히 데이터의 분포를 볼 수 있다. 셀프 주유소 말고 상표별 데이터를 확인했는데 SK에너지가 높은 가격대를 형성하는 주유소가 많았다. 전반적으로는 현대 오일뱅크가 4대 주유 브랜드 중에서는 저렴하다는 것을 확인할 수 있다.
In [64]:
stations
Out[64]:
  Oil_store 주소 가격 셀프 상표 구
0 (주)보성 세곡주유소 서울 강남구 헌릉로 731 (세곡동) 1435.0 Y SK에너지 강남구
1 GS칼텍스(주)직영 산성주유소 서울 강남구 헌릉로 730 1445.0 Y GS칼텍스 강남구
2 방죽주유소 서울 강남구 밤고개로 215 (율현동) 1459.0 Y GS칼텍스 강남구
3 오일프러스 셀프 서울 강남구 남부순환로 2651 (도곡동) 1463.0 Y SK에너지 강남구
4 현대오일뱅크 도곡셀프주유소 서울 강남구 남부순환로 2718 (도곡2동) 1473.0 Y 현대오일뱅크 강남구
... ... ... ... ... ... ...
520 SK논현주유소 서울 강남구 논현로 747 (논현동) 1938.0 N SK에너지 강남구
521 (주)만정에너지 서울 강남구 봉은사로 433 (삼성동) 1953.0 N GS칼텍스 강남구
522 학여울주유소 서울 강남구 남부순환로 3170 (일원2동) 1971.0 N GS칼텍스 강남구
523 (주)소모에너지엔테크놀러지 쎈트럴주유소 서울 강남구 삼성로 335 1988.0 N GS칼텍스 강남구
524 뉴서울(강남) 서울 강남구 언주로 716 2075.0 N SK에너지 강남구

525 rows × 6 columns

In [65]:
stations.boxplot(column='가격', by='셀프', figsize=(12,8))
plt.figure(figsize=(12,8))
sns.boxplot(x='상표', y='가격', hue='셀프', data=stations, palette='Set3')
plt.figure(figsize=(12,8))
sns.boxplot(x='상표', y='가격', data=stations, palette='Set3')
sns.swarmplot(x='상표', y='가격', data=stations, color='.6')
plt.show()
 
 
 
 
결과¶

현대 오일뱅크, GS칼텍스, S-Oil, SK에너지 모두 셀프 주유소가 저렴하다. SK에너지는 그 중 가격대가 가장 높게 형성되어 있는 것을 알 수 있다.

 
In [66]:
pip install folium
 
Requirement already satisfied: folium in c:\anaconda3\lib\site-packages (0.12.0)
Requirement already satisfied: numpy in c:\anaconda3\lib\site-packages (from folium) (1.19.2)
Requirement already satisfied: branca>=0.3.0 in c:\anaconda3\lib\site-packages (from folium) (0.4.2)
Requirement already satisfied: requests in c:\anaconda3\lib\site-packages (from folium) (2.24.0)
Requirement already satisfied: jinja2>=2.9 in c:\anaconda3\lib\site-packages (from folium) (2.11.2)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in c:\anaconda3\lib\site-packages (from requests->folium) (1.25.11)
Requirement already satisfied: chardet<4,>=3.0.2 in c:\anaconda3\lib\site-packages (from requests->folium) (3.0.4)
Requirement already satisfied: idna<3,>=2.5 in c:\anaconda3\lib\site-packages (from requests->folium) (2.10)
Requirement already satisfied: certifi>=2017.4.17 in c:\anaconda3\lib\site-packages (from requests->folium) (2020.6.20)
Requirement already satisfied: MarkupSafe>=0.23 in c:\anaconda3\lib\site-packages (from jinja2>=2.9->folium) (1.1.1)
Note: you may need to restart the kernel to use updated packages.
In [67]:
pip install googlemaps
 
Requirement already satisfied: googlemaps in c:\anaconda3\lib\site-packages (4.4.2)
Requirement already satisfied: requests<3.0,>=2.20.0 in c:\anaconda3\lib\site-packages (from googlemaps) (2.24.0)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in c:\anaconda3\lib\site-packages (from requests<3.0,>=2.20.0->googlemaps) (1.25.11)
Requirement already satisfied: idna<3,>=2.5 in c:\anaconda3\lib\site-packages (from requests<3.0,>=2.20.0->googlemaps) (2.10)
Requirement already satisfied: chardet<4,>=3.0.2 in c:\anaconda3\lib\site-packages (from requests<3.0,>=2.20.0->googlemaps) (3.0.4)
Requirement already satisfied: certifi>=2017.4.17 in c:\anaconda3\lib\site-packages (from requests<3.0,>=2.20.0->googlemaps) (2020.6.20)
Note: you may need to restart the kernel to use updated packages.
In [68]:
# 지도를 그리기 위한 모듈 import
import json
import folium
import googlemaps
import warnings
warnings.simplefilter(action='ignore', category = FutureWarning)
In [69]:
# 이제 서울시에서 가장 주유 가격이 비싼 주유소를 보겠다.
stations.sort_values(by='가격', ascending=False).head(10)
Out[69]:
  Oil_store 주소 가격 셀프 상표 구
343 서남주유소 서울 중구 통일로 30 2270.0 N SK에너지 중구
308 서계주유소 서울 용산구 청파로 367 (서계동) 2189.0 N GS칼텍스 용산구
342 필동주유소 서울 중구 퇴계로 196 (필동2가) 2129.0 N GS칼텍스 중구
341 SK에너지(주) 퇴계로주유소 서울 중구 퇴계로 228 (필동2가) 2099.0 N SK에너지 중구
524 뉴서울(강남) 서울 강남구 언주로 716 2075.0 N SK에너지 강남구
37 뉴서울(강남) 서울 강남구 언주로 716 2075.0 N SK에너지 강남구
306 한남지점 서울 용산구 한남대로21길 4 (한남동) 1998.0 N SK에너지 용산구
307 에너비스 서울 용산구 한남대로 82 (한남동) 1998.0 N SK에너지 용산구
332 (주)대양씨앤씨 사직주유소 서울 종로구 사직로 65 (사직동) 1998.0 N GS칼텍스 종로구
333 (주)중앙에너비스 혜화주유소 서울 종로구 창경궁로35길 1 1998.0 N SK에너지 종로구
In [70]:
# 서울시에서 가장 주유 가격이 싼 주유소
stations.sort_values(by='가격', ascending=True).head(10)
Out[70]:
  Oil_store 주소 가격 셀프 상표 구
114 만남의광장주유소 서울 서초구 양재대로12길 73-71 1379.0 Y 알뜰(ex) 서초구
265 남서울고속주유소 서울 영등포구 가마산로 367 (대림동) 1379.0 Y SK에너지 영등포구
266 도림주유소 서울 영등포구 도림로 343 (도림동) 1384.0 Y 알뜰주유소 영등포구
373 화곡역주유소 서울 강서구 강서로 154 (화곡동) 1387.0 Y 알뜰주유소 강서구
374 뉴신정주유소 서울 강서구 곰달래로 207(화곡동) 1387.0 N 알뜰주유소 강서구
267 (주)강서오일 서울 영등포구 도신로 151 1389.0 N 현대오일뱅크 영등포구
375 유턴주유소 서울 강서구 남부순환로 57 1395.0 Y 알뜰주유소 강서구
166 원천주유소 서울 성북구 돌곶이로 142 (장위동) 1398.0 N 알뜰주유소 성북구
167 강산제2주유소 서울 성북구 보국문로 52 1398.0 Y 현대오일뱅크 성북구
168 (주)삼표에너지 정릉주유소 서울 성북구 보국문로 35 (정릉동) 1398.0 Y GS칼텍스 성북구
In [71]:
# pivot_table을 이용해서 구별 가격 정보로 변경하고 가격은 평균값으로 정리하자.
import numpy as np
gu_data = pd.pivot_table(stations, index=['구'], values=['가격'],
                        aggfunc=np.mean)
gu_data.head()
Out[71]:
  가격
구  
강남구 1624.868421
강동구 1532.500000
강북구 1430.461538
강서구 1475.454545
관악구 1468.600000
In [72]:
# # 서울시 구별 정보에 대해 지도로 표현하자. 평균적으로 
# geo_path = './02. skorea_municipalities_geo_simple.json'
# geo_str = json.load(open(geo_path, encoding='utf-8'))

# map = folium.Map(location=[37.5502, 126.982], zoom_start=10.5, 
#                  tiles='Stamen Toner')

# map.choropleth(geo_data = geo_str,
#                data = gu_data,
#                columns=[gu_data.index, '가격'],
#                fill_color='PuRd', #PuRd, YlGnBu
#                key_on='feature.id')
# map
 

4-6 서울시 주유 가격 상하위 10개 주유소 지도에 표기하기¶

In [73]:
# 주유 가격 상위 10개 주소를 oil_price_top10 이름으로 저장한다.
oil_price_top10 = stations.sort_values(by='가격', ascending=False).head(10)
oil_price_top10
Out[73]:
  Oil_store 주소 가격 셀프 상표 구
343 서남주유소 서울 중구 통일로 30 2270.0 N SK에너지 중구
308 서계주유소 서울 용산구 청파로 367 (서계동) 2189.0 N GS칼텍스 용산구
342 필동주유소 서울 중구 퇴계로 196 (필동2가) 2129.0 N GS칼텍스 중구
341 SK에너지(주) 퇴계로주유소 서울 중구 퇴계로 228 (필동2가) 2099.0 N SK에너지 중구
524 뉴서울(강남) 서울 강남구 언주로 716 2075.0 N SK에너지 강남구
37 뉴서울(강남) 서울 강남구 언주로 716 2075.0 N SK에너지 강남구
306 한남지점 서울 용산구 한남대로21길 4 (한남동) 1998.0 N SK에너지 용산구
307 에너비스 서울 용산구 한남대로 82 (한남동) 1998.0 N SK에너지 용산구
332 (주)대양씨앤씨 사직주유소 서울 종로구 사직로 65 (사직동) 1998.0 N GS칼텍스 종로구
333 (주)중앙에너비스 혜화주유소 서울 종로구 창경궁로35길 1 1998.0 N SK에너지 종로구
In [74]:
# 역시 하위 10개에 대해서도 oil_price_bottom10에 저장한다.
oil_price_bottom10 = stations.sort_values(by='가격', ascending=True).head(10)
oil_price_bottom10
Out[74]:
  Oil_store 주소 가격 셀프 상표 구
114 만남의광장주유소 서울 서초구 양재대로12길 73-71 1379.0 Y 알뜰(ex) 서초구
265 남서울고속주유소 서울 영등포구 가마산로 367 (대림동) 1379.0 Y SK에너지 영등포구
266 도림주유소 서울 영등포구 도림로 343 (도림동) 1384.0 Y 알뜰주유소 영등포구
373 화곡역주유소 서울 강서구 강서로 154 (화곡동) 1387.0 Y 알뜰주유소 강서구
374 뉴신정주유소 서울 강서구 곰달래로 207(화곡동) 1387.0 N 알뜰주유소 강서구
267 (주)강서오일 서울 영등포구 도신로 151 1389.0 N 현대오일뱅크 영등포구
375 유턴주유소 서울 강서구 남부순환로 57 1395.0 Y 알뜰주유소 강서구
166 원천주유소 서울 성북구 돌곶이로 142 (장위동) 1398.0 N 알뜰주유소 성북구
167 강산제2주유소 서울 성북구 보국문로 52 1398.0 Y 현대오일뱅크 성북구
168 (주)삼표에너지 정릉주유소 서울 성북구 보국문로 35 (정릉동) 1398.0 Y GS칼텍스 성북구
In [75]:
# google maps API용 key 입력
gmap_key = 'AIzaSyD5J0tdagg-KxXLNRhkD8kTImlcAmMV1Pk'
gmaps = googlemaps.Client(key=gmap_key)
In [76]:
gmaps.geocode(tmp_add)
 
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-76-d5063ff21488> in <module>
----> 1 gmaps.geocode(tmp_add)

NameError: name 'tmp_add' is not defined
In [ ]:
oil_price_top10
In [ ]:
tmp_add = str(oil_price_top10['주소'][n]).split('(')[0]
tmp_add
In [ ]:
gmaps.geocode(tmp_add)
In [ ]:
# 주유 가격 상위 10개 주유소에 대해 위도, 경도 정보를 읽어오자.
# 구글 맵에서 검색할 수 없다든지 하는 문제로 에러가 나는 것에 대비해 try-except
# try 구문을 실행하다가 에러나면 except 구문에서 지정된 nan값을 저장

from tqdm import tqdm_notebook

lat = []
lng = []

for n in tqdm_notebook(oil_price_top10.index):
    try:
        tmp_add = str(oil_price_top10['주소'][n]).split('(')[0]
        print('1')
        tmp_map = gmaps.geocode(tmp_add)
        print('2')
        tmp_loc = tmp_map[0].get('geometry')
        print('3')
        lat.append(tmp_loc['location']['lat'])
        print('4')
        lng.append(tmp_loc['location']['lng'])
        print('5')
    except:
        lat.append(np.nan)
        lng.append(np.nan)
        print('Here is nan !')
        
oil_price_top10['lat']=lat
oil_price_top10['lng']=lng
oil_price_top10
In [ ]:
lat = []
lng = []

for n in tqdm_notebook(oil_price_bottom10.index):
    try:
        tmp_add = str(oil_price_bottom10['주소'][n]).split('(')[0]
        tmp_map = gmaps.geocode(tmp_add)
        
        tmp_loc = tmp_map[0].get('geometry')
        lat.append(tmp_loc['location']['lat'])
        lng.append(tmp_loc['location']['lng'])
        
    except:
        lat.append(np.nan)
        lng.append(np.nan)
        print('Here is nan !')
        
oil_price_bottom10['lat']=lat
oil_price_bottom10['lng']=lng
oil_price_bottom10
 
결과를 보면 '중구', '종로구'와 같은 서울 중심에 비싼 주유소가 몰려있고, '강북', '은평', '양천'과 같은 서울 바깥방향에 저렴한 주유소가 몰려 있다는 것을 알 수 있다.기름이 부족하다면 서울 안쪽으로 들어가기 전에 미리 넣고 가는 것이 좋다는 것을 알 수 있다.

# 위에서 혹시 nan값으로 처리된 부분이 있다면 , # notmul 명령을 이용해서 NaN이 아닐때만 지도에 표기하도록 한다. map = folium.Map(location=[37.5202, 126.975], zoom_start=10.5) for n in oil_price_top10.index: if pd.notnull(oil_price_top10['lat'][n]): folium.CircleMarker([oil_price_top10['lat'][n], oil_price_top10['lng'][n]], radius=15, color='#CD3181', fill_color='#CD3181').add_to(map) for n in oil_price_bottom10.index: if pd.notnull(oil_price_bottom10['lat'][n]): folium.CircleMarker([oil_price_bottom10['lat'][n], oil_price_bottom10['lng'][n]], radius=15, color='#3186cc', fill_color='#3186cc').add_to(map) map

'workSpace > PYTHON' 카테고리의 다른 글

[Matplot] Matplot Lib를 이용한 시각화  (0) 2021.01.18
[Pandas][Numpy][seaborn] 2019년 서울 특별시 아파트 실거래 분석 및 표 만들기  (0) 2021.01.18
[ML] SVM - 지도학습 분류모델 실습  (0) 2021.01.18
[ML] SVM - 지도학습 분류모델  (0) 2021.01.18
[ML][DL] 전반적인 이해도 및 로직 설명, 자료  (0) 2021.01.18
    'workSpace/PYTHON' 카테고리의 다른 글
    • [Matplot] Matplot Lib를 이용한 시각화
    • [Pandas][Numpy][seaborn] 2019년 서울 특별시 아파트 실거래 분석 및 표 만들기
    • [ML] SVM - 지도학습 분류모델 실습
    • [ML] SVM - 지도학습 분류모델
    J o e
    J o e
    나의 과거를 기록합니다.

    티스토리툴바