Category Archives: Data Analysis

[Coursera] Applied Data Science with Python 수강 시작

Python 데이터 사이언스 과목을 Coursera에서 수강하기 시작했습니다.

이직 한 뒤로 계속해서 front-end만 보다보니, front-end를 싫어하는 건 아니지만 조금 현기증이 나려고 합니다 -_-;

원래 취미로 하던 Python 데이터 분석을 조금씩 놓고 있는 것 같아,

감도 잡고 강제로 공부도 할 겸 수강을 시작합니다.

 

전에 안좋은 추억이 있었던 만큼, 이번에는 다 결제하지 않고 하나씩 결제하려고 합니다.

일단 첫인상은 나쁘지 않네용.

 

배운 내용 종종 포스팅하겠습니다.

[Python] 서울시 기상관측 데이터 갖고 놀기 (by pandas)

일을 해야 하는데 일을 주질 않는다. 방치되고 있다.

그래서 심심하니까 pandas를 배울겸 장난질을 해보자.

계속 pandas로 장난치는 이유는 주식/금융 정보를 분석할 때 쓰이는 요긴한 툴이기 때문이다.

 

오늘 장난 칠 데이터는 서울시 일별 기상관측 정보다. 생각보다 데이터 양이 많지는 않아서 아쉽다. 2015년 정보부터 있다. (지난1년간데이터)

그래도 pandas와 matplotlib를 배운다는 의미에서는 재밌는 자료다.

일단 자료를 불러와서 세팅을 해보자.

import pandas as pd

weather_csv = "seoulweather.csv"
data = pd.read_csv(weather_csv, index_col=None, header=1)
data.columns = (['관측일자', '구', '평균기온', '최저기온', '최대기온', '평균습도', '최저습도', '최고습도', '평균풍속', '최대풍속', '강수량'])

기본적인 데이터 준비는 끝났다.

간단한 장난부터 시작해보자.

 

  1.  가장 많이 비가 온날은 언제인가?
    max_rain = data.sort_values(by=['강수량'], ascending=[False])
    print(max_rain[:1])
    
    관측일자   구  평균기온  최저기온  최대기온  평균습도  최저습도  최고습도  평균풍속  최대풍속   강수량
    8525  20150617  금천  22.5  19.9  28.0  81.4  61.9  96.7   1.9   4.6  57.0

    지난 1년간 가장 비가 많이 온날은 2015년 6월 17일 금천구다. 장마철인가? 57mm의 비가 왔다는 걸 알 수 있다.

  2. 지역구 별 평균 기온?
    ku_temp_mean = data.groupby(["구"]).mean()
    ku_temp_mean = ku_temp_mean.drop('관측일자', 1)
    ku_temp_mean = ku_temp_mean.sort_values(by=['평균기온'], ascending=[False])
    
    print(ku_temp_mean['평균기온'])
    구
    동작     14.190323
    서초     14.128153
    강동     14.076833
    종로     14.063930
    양천     13.995308
    광진     13.993548
    중랑     13.984132
    성동     13.915964
    송파     13.871848
    노원     13.848680
    동대문    13.828739
    금천     13.787683
    강북     13.761290
    관악     13.740176
    성북     13.706452
    구로     13.680938
    영등포    13.663343
    강서     13.593548
    강남     13.512023
    마포     13.345455
    서대문    13.135777
    은평     13.087683
    용산     13.067314
    남산     12.980938
    도봉     12.947214
    중구     12.909384

    지난 1년 데이터 가지고 뭐라하기는 그렇지만, 가장 더운 곳은 동작구, 가장 추운 곳은 중구다. 의외다. 난 중구가 지열때문에 가장 뜨거울줄 알았는데. 문과생 의문의 1패

  3. 서울에서 가장 바람이 세게 부는 달은?
    요건 조금 손이간다. 일단 관측일자를 month로 groupby한다음에 정렬을 해야할 것이다.

    month_data = data
    month_data.관측일자 = pd.to_datetime(month_data.관측일자, format='%Y%m%d')
    month_data = month_data.set_index(['관측일자'])
    month_data = month_data.groupby(pd.TimeGrouper("M")).mean()
    month_data = month_data.sort_values(by=['최대풍속'], ascending=[False])
    print(month_data)
    평균풍속      최대풍속       강수량  
    관측일자                                      
    2016-04-30  1.709814  5.707294  2.603448  
    2016-02-29  1.760933  5.600533  1.392667  
    2016-05-31  1.685947  5.422189  4.777367  
    2016-03-31  1.682112  5.289317  1.168944  
    2015-07-31  1.730769  4.889744  4.330769  
    2015-10-31  1.412159  4.872084  2.624690  
    2015-06-30  1.676374  4.800549  1.913004  
    2016-01-31  1.607760  4.695995  0.001252  
    2015-09-30  1.529003  4.656168  0.733596  
    2015-08-31  1.551083  4.515884  1.222924  
    2015-11-30  1.502692  4.496538  3.853205  
    2015-12-31  1.403102  4.403226  0.765509

    급하게 하다가 몇 개 까먹은게 있는데 여기서 작업했다. 일단 인덱스 컬럼을 날짜로 했었어야했고, 그리고 그 인덱스컬럼을 date로 parse 했어야 했다. 어쨌거나 그작업을 여기서 했다. 데이터로 미루어 보아 바람이 가장 빡세게 부는 달은 4월이다.

  4. 평균기온 그래프를 그려보자
    ku_temp_graph = data
    ku_temp_graph.관측일자 = pd.to_datetime(ku_temp_graph.관측일자, format='%Y%m%d')
    ku_temp_graph = ku_temp_graph.set_index(['관측일자'])
    ku_temp_graph["평균기온"].plot(legend=True)

    그래프가 그려진다. ㅠㅠ

[Python] 서울시열린데이터 – 서울시 공공 와이파이정보 파이썬으로 갖고 놀기(2)

이거의 두번째 시간이다. 사실 이거를 할 생각은 없었는데, Flask를 하게 되면서 이걸 좀더 심도 있게 가지고 놀 수 있게 되었다.

from flask import (Flask, render_template)
import pandas as pd

app = Flask(__name__)


@app.route("/")
def home():
    map_path = "seoulwifi.csv"
    data = pd.read_csv(map_path, index_col=None, header=1)
    data.columns = (['구', '유형', '위치명', 'lat', 'lon', '설치회사'])

    # 서울 중심
    seoul_center = {
        "lat": 37.541,
        "lon": 126.986,
        "zoom": 13
    }
    result_data = []
    for index, row in data.iterrows():
        rounded_lat = round(row.lat, 8)
        rounded_lon = round(row.lon, 8)
       # 거지같은 코드 추가
        if rounded_lat > rounded_lon:
            temp = rounded_lat
            rounded_lat = rounded_lon
            rounded_lon = temp

        data = {
            "marker_text": row.위치명 + " by " + row.설치회사,
            "lat": rounded_lat,
            "lon": rounded_lon
        }
        result_data.append(data)

    return render_template("seoul.html", data=result_data, center=seoul_center)


if __name__ == "__main__":
    app.run(port=5555, debug=True)

정보를 가져와서 파싱하는 것 까지는 전과 똑같다.

다만 몇가지 골 때렸던건, 파싱할때는 몰랐던 건데, 일부 x, y 좌표 (lat, lon)가 뒤집혀서 제공되고 있었다는 것.

지도에 마커가 안떠서 한참이나 고생했는데, 설마 그런 이유 때문인지는 몰랐다.

<!DOCTYPE html>
<html>
  <head>
    <title>Seoul WIfI map</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta charset="utf-8">
      <style>
          #map {
            height: 800px;
          }
      </style>
  </head>
  <body>
    <h1> Seoul Public Wifi Map </h1>
    <div id="map"></div>
    <script>
    var map;
    var marker;
    function initMap() {
      map = new google.maps.Map(document.getElementById('map'), {
        center: {lat: {{center.lat}}, lng: {{center.lon}}},
        zoom: {{center.zoom}}
      });
      placeWIFI({{data | safe}})

    }
    function placeWIFI(data){

      for (i = 0; i < data.length; i++){
        marker = new google.maps.Circle({
          center: new google.maps.LatLng(data[i].lat, data[i].lon),
          title: data[i].marker_text,
          map: map,
          radius:50,
          strokeColor:"#5A0001",
          strokeOpacity:0.8,
          strokeWeight:1,
          fillColor:"#F45B69",
          fillOpacity:0.6
        })
        # 마우스 오버 아웃이벤트
        google.maps.event.addListener(marker,'mouseover',function(){
             this.getMap().getDiv().setAttribute('title',this.get('title'));});

        google.maps.event.addListener(marker,'mouseout',function(){
             this.getMap().getDiv().removeAttribute('title');});
      }
    }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_APU_KEY" async defer></script>
  </body>
</html>

마커 대신에 원을 그렸다. 보통 와이파이가 50m 까지 영향을 미친 다고 하니까, 그래서 radius를 50으로 설정했다.

그리고 Google 맵의 circle마커는 마우스 오버 이벤트(마커의 타이틀을 보여주는 것) 가 없기 때문에 수동으로 오버와 아웃이벤트를 위와 같이 추가해줘야 한다.

결과

seoulwifimap

마우스 올리면 타이틀도 뜹니다.

캡쳐를 못해가지고 그렇지.

[Python] 서울시열린데이터 – 서울시 공공 와이파이정보 파이썬으로 갖고 놀기

너무 삭막하고 어렵고 머리굴려야만 하는 주제로만 해서 좀 가벼운 주제로 장난을 쳐볼까 생각했다.

서울시 열린 데이터를 갔더니 가장 인기있는(조회수가 많은) 자료가 서울시공공와이파이위치정보 이길래, 이걸 가지고 python, numpy, pandas, matplotlib로 간단한 통계를 내볼까 한다.

일단 기본 설정

위 경도 정보가 있는데, 이 정보로 지도를 가져오기 위해 googlemaps 라이브러리를 사용했다.

import json
import pandas as pd
import googlemaps

import matplotlib.pyplot as plt

%matplotlib inline

path = "seoul_wifi.csv"
# 이 api는 https://console.developers.google.com 에 가서 google geocoding api - 서버키를 가져오면 된다.
gmaps = googlemaps.Client(key="YOUR_GOOGLE_API_KEY")

데이터를 가져와 보자.

data = pd.read_csv(path, index_col=None, header=1)
GU_NM CATEGORY PLACE_NAME INSTL_X INSTL_Y INSTL_DIV
0 강남구 공공기관 (재)서울산업통상진흥원 127.071755 37.495582 LGU+
1 강남구 공공기관 (재)서울산업통상진흥원서울신기술창업센타 127.038054 37.497612 LGU+
2 강남구 공공기관 U강남도시관제센터 127.040992 37.508403 강남구
3 강남구 공공기관 강남구의회 127.064203 37.493938 강남구

 

컬럼 정보를 알아보기 쉽게 바꾸자.

data.columns = (['구', '유형', '위치명', 'lat', 'lon', '설치회사'])

이 정보만 가지고는 실제로 이 와이파이가 정확히 어떤 주소에 있는지 알기 힘들다.

그래서 아까 언급했던 라이브러리를 이용하여 위,경도로 실제 주소를 가져와보려고 한다.

list_cctv_address = []
for index, row in data.iterrows():
    # 이유는 잘 모르지만 경도, 위도 순으로 넣는 reverse_geocode를 사용해야 한다. 그냥 geocode는 다른 용도가 있다.
    # 이런식으로 주소를 가져온다.
    sample = gmaps.reverse_geocode((row.lon, row.lat), language="ko")
    result = sample[0].get("formatted_address")
    list_cctv_address.append(result)
    # 위경도 하나당 Google api 를 호출하기 때문에 다 가져오려면 굉장히 느리다.
    if index == 50:
        break
list_cctv_address
['대한민국 서울특별시 강남구 대치2동 514',
 '대한민국 서울특별시 강남구 역삼1동 746',
 '대한민국 서울특별시 강남구 역삼1동 687-10',
 '대한민국 서울특별시 강남구 대치2동 509-2',
 '대한민국 서울특별시 강남구 학동로 426',
 '대한민국 서울특별시 강남구 도곡동 459-4',
 '대한민국 서울특별시 강남구 테헤란로114길 23',
 '대한민국 서울특별시 강남구 도곡1동 963-1',
 '대한민국 서울특별시 강남구 테헤란로 410',
 '대한민국 서울특별시 강남구 역삼1동 635-2',
 '대한민국 서울특별시 강남구 도곡2동 467-12',
 '대한민국 서울특별시 강남구 역삼동 635-4',
 '대한민국 서울특별시 강남구 역삼2동 708-1',
 '대한민국 서울특별시 강남구 일원본동 산4-1',
 '대한민국 서울특별시 강남구 논현1동 150-5',
 '대한민국 서울특별시 강남구 논현1동 162-11',
 '대한민국 서울특별시 강남구 논현1동 47-13',
 '대한민국 서울특별시 강남구 일원동 706-4',
 '대한민국 서울특별시 강남구 도곡2동 415-3',
 '대한민국 서울특별시 강남구 도곡2동 461',
 '대한민국 서울특별시 강남구 도곡2동 420-10', ........................

주소를 잘 가져온다.

물론 나중에 web과 연동해서 마커를 찍는 일도 해볼 수 있을 것이다.

 

이번엔 구 별로 몇개의 cctv 가 있는지 막대그래프로 그려보자.

district_ku = dict();

for index, row in data.iterrows():
    district = row.구
    
    if district_ku.get(district) is None:
        district_ku[district] = 0
    else:
        district_ku[district] += 1
        
district_ku
{'강남구': 281,
 '강동구': 78,
 '강북구': 119,
 '강서구': 121,
 '경기도': 15,
 '관악구': 144,
 '광진구': 126,
 '구로구': 140,
 '금천구': 57,
 '노원구': 143,
 '도봉구': 65,
 '동대문구': 110,
 '동작구': 85,
 '마포구': 102,
 '서대문구': 108,
 '서초구': 95,
 '성동구': 93,
 '성북구': 103,
 '송파구': 99,
 '양천구': 113,
 '영등포구': 105,
 '용산구': 122,
 '은평구': 122,
 '종로구': 183,
 '중구': 167,
 '중랑구': 72}

네이밍 센스가 거지 같다. 어쨌거나, 역시 강남구에 cctv가 제일 많다.

생각해보니 0 이 아니라 1 부터 시작했어야 했는데. 졸려서 실수했습니다 지송

 

이제 이걸 pandas의 dataframe으로 변환해서 그래프를 그려보자.

result_frame = pd.DataFrame.from_dict(district_ku, orient='index')
result_frame.columns = (["wifi 숫자"])
result_frame.plot(kind='bar', label="구", legend=True, title="서울시 구별 wifi 통계");

근데 왜 경기도가 나오는걸까? 아마 경기도에 서울시 wifi가 있는 모양이다.

확인해봅시다.

kyungido = "경기도"

for index, row in data.iterrows():
    district = row.구
    if str(district) == kyungido:
        print(data.values[index])
['경기도' '공원' '서울_대공원' 127.014195 37.436319 '서울시']
['경기도' '공원' '서울대공원1' 127.011201 37.433846 'SKT']
['경기도' '공원' '서울대공원1' 127.017972 37.427240999999995 'SKT다']
['경기도' '공원' '서울대공원1' 127.01884799999999 37.426916 'SKT']
['경기도' '공원' '서울대공원1' 127.019582 37.427655 'SKT']
['경기도' '공원' '서울대공원1' 127.01978500000001 37.423573 'SKT']
['경기도' '공원' '서울대공원1' 127.02236599999999 37.421996 'SKT']
['경기도' '공원' '서울대공원1' 127.024178 37.419378 'SKT']
['경기도' '공원' '서울대공원2' 127.011201 37.433846 'SKT']
['경기도' '공원' '서울대공원2' 127.01530100000001 37.429971 'SKT']
['경기도' '공원' '서울대공원2' 127.017972 37.427240999999995 'SKT']
['경기도' '공원' '서울대공원2' 127.01884799999999 37.426916 'SKT']
['경기도' '공원' '서울대공원2' 127.019582 37.427655 'SKT']
['경기도' '공원' '서울대공원2' 127.01978500000001 37.423573 'SKT']
['경기도' '공원' '서울대공원2' 127.02236599999999 37.421996 'SKT']
['경기도' '공원' '서울대공원2' 127.024178 37.419378 'SKT']

과천에 있는 서울대공원 와이파이는 서울에서 관리한다는 것을 알 수 있게 되었다. 아주 중요한 자료가 아닐 수 없다.

마지막으로 서울시 공공 와이파이를 세운 회사들의 비중을 알아보자.

wifi_corp = dict();

for index, row in data.iterrows():
    district = row.설치회사
    
    if wifi_corp.get(district) is None:
        wifi_corp[district] = 1
    else:
        wifi_corp[district] += 1

result_frame = pd.DataFrame.from_dict(wifi_corp, orient='index')
result_frame.columns = (["corp"])
result_frame

LG가 서울시에 가장 많은 조공을 바친 것을 알 수 있다.

정말 별거 아니었지만 그냥 연습하기에 좋은 예제 였다.

 

참고)

주소로 해당 위치에 대한 정보를 알아보려면 다음과 같이 하면 된다.

geocode_result = gmaps.geocode('대한민국 서울특별시 강남구 대치2동 514', language='ko')

 

[{'address_components': [{'long_name': '514',
    'short_name': '514',
    'types': ['premise', 'political']},
   {'long_name': '대치2동',
    'short_name': '대치2동',
    'types': ['sublocality_level_2', 'sublocality', 'political']},
   {'long_name': '강남구',
    'short_name': '강남구',
    'types': ['sublocality_level_1', 'sublocality', 'political']},
   {'long_name': '서울특별시',
    'short_name': '서울특별시',
    'types': ['locality', 'political']},
   {'long_name': '대한민국',
    'short_name': 'KR',
    'types': ['country', 'political']},
   {'long_name': '135-282',
    'short_name': '135-282',
    'types': ['postal_code']}],
  'formatted_address': '대한민국 서울특별시 강남구 대치2동 514',
  'geometry': {'location': {'lat': 37.495846, 'lng': 127.0722393},
   'location_type': 'ROOFTOP',
   'viewport': {'northeast': {'lat': 37.5046982, 'lng': 127.0882467},
    'southwest': {'lat': 37.4869928, 'lng': 127.0562319}}},
  'place_id': 'ChIJpS4nCDCkfDURkEAwb_AW3iU',
  'types': ['premise', 'political']}]

 

[Python] Matplotlib 한글 설정

matplotlib.pyplot 으로 그래프를 그리면 한글이 다 깨져서 나온다.

이는 matplotlib에 적절한 한글 폰트 세팅이 안되어 있기 때문이다. (찾느라 고생했다 ㅠㅠ)

 

matplotlib.font_manager.get_fontconfig_fonts()

명령어로 사용가능한 폰트 목록을 살펴본다음, 아래와 같이 폰트를 설정해주면 된다.

krfont = {'family' : 'nanumgothic', 'weight' : 'bold', 'size'   : 10}
matplotlib.rc('font', **krfont)

이제 이렇게 아름답게 한글이 적용되서 나오는 것을 볼수가 있다.

 

참고로 새롭게 추가한 폰트가 보이지 않는다면, 이는 matplotlib의 fontcache에 아직 추가가 안되어 있기 때문이다.

$HOME/.cache/matplotlib

여기에 가서, font관련 cache 를 다 지우고 다시 실행 시키면 된다.

그럼 잠시간의 시간이 흐른뒤에, 새롭게 폰트 캐시가 설정된다.