심심해서 하는 블로그 :: 심심해서 하는 블로그

마라탕 첫 느낌

요새 감자님이 "맛있는 녀석들" 을 왓챠플레이를 보면서 정주행 중인데, 그거 덕분에 데이트 메뉴가 다채로워 지는거 같았습니다.

감자님은 원래 매운 걸 먹는 걸 좋아해서 서울에 맵다는 곳은 다 돌아다니면서 먹었어요.

물론 항상 약국에서 위장약 사와서 힘들어 하기도 했었구요





그래서 마라탕을 먹는다고 했을 때 겁도 많이 났었지만, 매운 걸 잘 못 먹는 지인이 중국 여행을 다녀오고 나서 맛있다고 자랑을 해서

그래, 한 번 먹어보자는 생각으로 갔던거 같아요.

마라탕을 먹으러 갔는데 주변에 생선을 엄청나게 굽고 있더라고요.

생선구이 골목이라고 하는데 연기가 자욱했지만 그 곳도 궁금하긴 하네요


매운 단계는 0단계부터 아주 맵다는 4단계 총 5단계로 되어있어요

저희 둘은 양고기 3단계와 소고기 3단계 마라탕을 주문하고, 면은 옥수수면이 좋다해서 옥수수면으로 했어요.

사이드 메뉴로 쇼좌빙이라는 걸 주문했어요.





고소한 땅콩향과 알싸한 맛

3단계를 시켜서 맵지 않을까 걱정했는데, 신라면 국물 정도로 매웠어요.

국물은 땅콩 그 자체였기 때문에 땅콩 알르레기가 있는 사람이라면 미리 말하는게 좋습니다.




양고기나 소고기나 국물맛이 크게 다르지는 않았어요. 양고기를 더 좋아하는 취향이면 양고기를 드셔도 될거같아요

맨날 탱글탱글한 면을 먹다가 축쳐진 옥수수면을 먹으니까 익숙하지 않은 식감이였어요.

다음 번에는 다른 면을 먹어 봐야겠어요. 개인적으로 옥수수 면은 식감때문에 썩 좋지는 않았습니다.




감자님이 고수를 먹을 줄 알아서 고수를 넣어서 먹었는데 그냥 먹는 거보다 맛있다고 꼭 넣어 먹어라는데

저는 고수를 싫어하기 때문에, 전혀 공감가지 않았습니다 ㅋㅋㅋ

고수를 좋아하는 분들은 한 번 넣어서 드셔보세요. 고수는 셀프로 먹고 싶은 만큼 퍼서 드실 수 있어요.




쇼좌빙이 왔는데 크로와상을 넓게 펼친 듯한 비주얼이였어요. 연유와 함께 주는데 별미였어요.

야채 크래커 향과 연유에 달달한게 만나서 맛이 좋더라구요.

뭔지 궁금해서 호기심에 지른 건데 조금 기름지긴 했지만 맛이 좋아서 기분이 좋았습니다.



먹을 땐 괜찮았는데..

먹다 보니까 점점 매워지긴 했지만 이 정도는 견딜만하다 생각했었는데, 위장은 그렇지 않았나봐요 ㅋㅋㅋㅋㅋ

저녁에 감자님이랑 저랑 각자 화장실에서 고생을 좀 많이 했습니다.

매운 걸 먹을 때마다 다음 날에 심하게 고생하는 타입이라면 괜한 용기 내지 마시고, 단계를 낮춰서 드시는게 좋을거 같네요.




긴 글 읽어 주셔서 감사합니다.

공감 버튼은 작성자에게 큰 힘이 됩니다. 감사합니다.


,

위치 : 한가람 미술관

전시일 : 2018.12.06(목) ~ 2019.03.10(일)

관람시간(~2월) : 오전 11시 - 오후 7시 (입장마감 오후 6시)

관람시간(3월) : 오전 11시 - 오후 8시 (입장마감 오후 7시)


이번 주말에는 존 레논 이매진 전시회를 다녀왔습니다.

예술의 전당에서 매번 커피나 마셨지 전시회를 직접 찾아 간적이 없어서, 아쉬웠는데 위메프에서 할인하는 것을 보고 후다닥 예매를 했습니다.

존 레논이 음악사의 한 시대를 풍미했던 인물이였기 때문에 사람들이 많이 방문했습니다.


저는 어릴 적 "20세기를 빛낸 사람들" 이라는 만화책에서 비틀즈 편으로 존 레논의 존재를 처음으로 알게 되었습니다.

그 때 느낀 존 레논의 이미지는 "제멋대로, 독특한 사상을 가진" 사람으로 느껴졌습니다.

다른 여자에 빠져 가정에 충실하지 않고 그룹 맴버들과 서먹해하는 그가 이기적이라고 생각도 했었습니다.

20대가 된 지금은 어떻게 받아드려질까 생각하면서 전시회를 관람하였습니다.


감자님은 걱정이 많았습니다.

비틀즈의 몇몇 노래는 무한도전을 통해서 들었지만, 맴버가 누가 있고 그들에 대한 정보를 전혀 몰랐기 때문입니다.

그래서 도슨트를 이용할까 하고 이용시간을 확인하였습니다.


도슨트 제공 시간

월 화 : 11:30 / 14:00 / 17:00

수 목 금 : 11:00 / 13:00 / 15:00 / 17:00

(주말에 더 이상 도슨트 서비스를 제공하지 않는다고 한다.)


입구 쪽에서 천천히 관람을 하면서 도슨트 분을 기다렸는데, 피리부는 사나이 마냥 우루루 모이는 것을 보고 포기했습니다.

물론 음성으로 안내하는 앱이나 음성안내 장치를 전시회에서 제공하니, 이 기회에 존 레논에 대해서 알아 보겠다고 생각하시는 분이라면 좋은 선택지가 되겠습니다.


존 레논의 사망으로 시작

전시회의 시작은 존 레논이 광팬이였던 마크 채프먼의 총을 맞고 사망한 사건에서부터 시작합니다.

뉴스 속보와 당시 슬퍼하는 수많은 사람들의 비통한 표정들이 존 레논이라는 인물이 얼마나 사랑을 받았는지 알 수 있었습니다.

(참고로 마크 채프먼은 종신형을 받고 아직 수감생활 중입니다.)


존 레논과 비틀즈의 탄생 그리고 성공


다음 관에서는 존 레논의 어릴 적 가정사와 비틀즈의 탄생에 대해 얘기합니다.

어느 시대이든 고난을 극복하는 사람이 시대를 대표하는 위인이 될 수 있나 봅니다.

어린 시절 존 레논은 하나뿐인 어머니를 교통사고로 잃는 등 힘든 시절을 보냅니다.


폴 매카트니와 조지 해리슨과 함께 밴드 생활을 하던 그들의 재능을 브라이언 엡스타인이 끈질기게 구애를 합니다.

설득이 끝에 결국 그가 매니저로 임명되었으며, 마지막에 합류한 드러머 링고 스타까지 합류하면서 우리가 아는 비틀즈가 활동을 시작합니다.

비틀즈는 그 후 내는 앨범마다 유럽과 미국에서 크게 성공을 하는 얘기들이 있습니다.




오노 요코와 만남


그의 인생은 오노 요코를 만남으로 크게 변화합니다.

존 레논과 오노 요코는 예술적인 영감 교류로 그들의 사상을 확장하지만, 비틀즈 맴버들과 거리가 멀어지면서 비틀즈는 해체합니다.

그 후 사랑과 평화라는 사상을 존 레논의 영향력으로 다양한 운동을 통해 세상을 향해 표출합니다.

"BED-IN", "bagism" 등 존 레논이 했던 행위 예술들을 전시회에서 체험해 볼 수 있습니다.



아빠 존 레논



존 레논이 오노 요코 사이에서 태어난 아이를 위해 5년간 육아를 하면서 그린 그림들과 사진들이 전시되어 있습니다.

그림들이 간결한 선들로 되어 있으나 인물의 특징을 잘 표현되어 있습니다.

작곡 능력에 그림과 시까지 지금까지 살아 있다면 더 많은 작품들을 남겼을텐데 하는 아쉬움이 들었습니다.

그리고 존 레논의 사진들이 전시되어 있는데, 남자가 봐도 잘 생겼습니다.



잘생겼다..


마치며 : Imagine


전시회의 마지막에는 피아노 한 대와 Imagine 뮤직 비디오가 틀어진 스크린이 있습니다.

Imagine 노래를 들으면서 저는 존 레논이 "몽상가(Dreamer)" 라고 생각이 들었습니다.

하지만 몽상가가 결코 나쁘다고 생각은 하지 않습니다.


비행기가 "내가 하늘 날 수 있으면 좋겠다" 꿈에서 시작되어 발명이 되었듯이,

그가 꿈꾸던 사랑과 평화가 가득한 세상을 우리가 함께 꿈꾼다면 인류는 언제나 그렇듯 꿈을 이루리라 생각합니다.

각박한 세상 살이에 서로를 탓하기 바쁜 우리 세상도 사랑으로 가득하길 기원합니다.


참고사항

  • 하나하나 꼼꼼하게 읽으면서 작품을 관람하니까 1시간정도 걸렸습니다.
  • 사진을 찍는 것은 자유로우나 동영상을 촬영하는 것은 금지되어 있습니다.
  • 매 챕터마다 비틀즈 또는 존 레논의 음악을 틀어 줍니다.


사진들







그래서 나는 오늘도 즐겁게 시간을 낭비했습니다 ^~^


어느 전시회처럼 기념품도 제공합니다.

감자님한테 등짝 맞을까봐 참았습니다.


비틀즈하면 떠오는 장면 중 하나. 

여기서 사진을 찍는 사람들이 정말 많습니다.


긴 글 짧은 글로 뽑을 수 있는데 존레논 외의 다른 사람이 한 명언들도 있다.



전쟁 끝!! 너가 원한다면!!



필름 속의 존 레논과 오노 요코

필름 사진기로 사진을 찍어야 누릴 수 있는 감성



Hey Jude를 부르는 폴 매카트니




긴 글 읽어 주셔서 감사합니다.

공감버튼은 작성자에게 큰 동기 부여가 됩니다.

,

GitHub : https://github.com/ssooni/data_mining_practice


새로운 시리즈 시작!!

이전 카카오 시리즈에 이어서 미세먼지 데이터와 기상 데이터를 이용해서 데이터 마이닝 기법들을 적용하는 연습을 해보았습니다.

(그거 보다 궁금했어요.. 기상과 미세먼지와 밀접한 관계가 있는지 말이죠)

데이터 수집, 전처리, 상관 분석 등 적용 해보고 싶은 방법들을 적용하고 결과를 관찰하도록 하겠습니다.


미세먼지 데이터 수집



서울시 열린데이터광장(http://data.seoul.go.kr/)에서 시간별 평균 대기오염도 정보 데이터를 제공합니다.

서울의 구 단위로 시간단위 미세먼지 농도를 제공하고 2009년 데이터도 존재하길래 사용하기 적절하다고 판단하여 인증 키를 취득하였습니다.

회원가입 절차를 진행한 후에 간단하게 사용 목적을 명시하면 인증 키를 획득할 수 있었습니다.

데이터의 저작권은 출처를 밝히면 자유롭게 사용이 가능합니다.




기상 데이터 수집


열린데이터광장에 서울시의 과거 기상데이터도 제공하지만, 데이터가 썩 만족스럽지가 않아서 기상자료개방포탈(https://data.kma.go.kr/cmmn/main.do)에서 기상데이터를 수집하였습니다.

기상포탈에는 Open-API 서비스로 다양한 종류의 기상 데이터를 제공합니다.

저는 시간 단위로 측정된 미세먼지 데이터를 수집하였기 때문에 기상 데이터도 종관기상관측 시간자료 를 이용하였습니다.

마찬가지로 회원가입을 진행한 후에 API 사용 신청을 하면 별다른 승인 절차 없이 API 사용이 가능합니다.

API Key는 마이페이지 > 오픈 API 현황에서 확인 가능합니다.




API 호출 프로그램 작성

전체 소스코드는 GitHub를 참조바랍니다.

API Key 및 전체 데이터의 경우 공유가 힘든 점 양해바랍니다.


REST API는 Http URL의 요청을 처리하여 우리가 보통 사용하는 WEB 서비스는 Response를 HTML로 하는 것과 달리 JSON이나 XML 등의 형태로 Response하는 API 입니다.

Python에는 requests 모듈이 있어서 http url만 입력하면 reponse를 획득할 수 있습니다.


미세먼지 데이터 API

먼저 서울시 시간별 평균 대기오염도 정보 데이터 API를 호출하는 프로그램을 작성하였습니다.

URL에 특정일자를 넣어주면 사용자가 설정한 start_index에서 end_index까지 데이터를 불러오는 API 구조입니다.

list_total_count 라고 해당 호출의 최대 index 값을 제공하므로 아래의 절차로 프로그램을 구상하였습니다.


  1. 최초 호출에서 list_total_count를 획득한다.
  2. 다음 호출에서 end_index 값을 list_total_count까지 사용한다.
  3. 날짜별로 데이터를 CSV로 저장한다.

## callAPI.py
def call_api(api_name, start_date, end_date, dir_name):
    # API 키는 공개하기 힘든 점 양해 바랍니다.
    api_key = open("./raw_data/api_key").readlines()[0].strip()
    url_format = 'http://openAPI.seoul.go.kr:8088/{api_key}/json/{api_name}/1/{end_index}/{date}'
    headers = {'content-type': 'application/json;charset=utf-8'}

    for date in pd.date_range(start_date, end_date).strftime("%Y%m%d"):
        # 최초 1회 Call은 해당 일자의 데이터 수를 확인한다.
        url = url_format.format(api_name=api_name, api_key=api_key, end_index=1, date=date)
        response = requests.get(url, headers=headers)
        end_index = response.json()[api_name]["list_total_count"]
        print("Max Count(%s): %s" % (date, end_index))

        # 해당 일자의 모든 데이터를 불러온다.
        url = url_format.format(api_name=api_name, api_key=api_key, end_index=end_index, date=date)
        response = requests.get(url, headers=headers)
        result = pd.DataFrame(response.json()[api_name]["row"])

        # 수집된 데이터를 CSV로 저장합니다.
        result.to_csv("./raw_data/%s/dust_%s.csv" % (dir_name, date), index=False, encoding="utf-8")

        # API 부하 관리를 위해 0.5초 정도 쉬어 줍시다 (찡긋)
        sleep(0.5)





기상 데이터 API


다음은 기상데이터 API를 사용하는 함수를 작성하였습니다.

이 API는 조회 시작 일자와 시간 / 최종 일자와 시간, 가져올 데이터의 수를 Param으로 사용합니다.

안전하게 저는 1회 요청시 하루 단위로 넉넉하게 최대 100건 정도를 요청하였습니다.

매 시간 단위로 측정되는 데이터라 많아야 24건이기 때문이죠.

결과는 마찬가지로 CSV 파일로 저장하였습니다.


## callAPI.py
def call_weather_api(start_date, end_date):
    # API 키는 공개하기 힘든 점 양해 바랍니다.
    api_key = open("./raw_data/weather_api").readlines()[0].strip()
    url_format = 'https://data.kma.go.kr/apiData/getData?type=json&dataCd=ASOS&dateCd=HR&startDt={date}&startHh=00&endDt={date}&endHh=23&stnIds={snt_id}&schListCnt=100&pageIndex=1&apiKey={api_key}'

    headers = {'content-type': 'application/json;charset=utf-8'}
    for date in pd.date_range(start_date, end_date).strftime("%Y%m%d"):
        print("%s Weather" % date)
        url = url_format.format(api_key=api_key, date=date, snt_id="108")
        response = requests.get(url, headers=headers, verify=False)
        result = pd.DataFrame(response.json()[-1]["info"])
        print(result.head())
        result.to_csv("./raw_data/weather/weather_%s.csv" % date, index=False, encoding="utf-8")

        # API 부하 관리를 위해 0.5초 정도 쉬어 줍시다 (찡긋)
        sleep(0.5)



데이터 종합

미세먼지와 기상 데이터를 2009년 1월 1일부터 2019년 1월 1일 구간으로 수집하였습니다.

두 개의 데이터는 공통적으로 날짜 컬럼이 있기 때문에 날짜 컬럼을 기준으로 Join을 해주었습니다.

데이터의 양이 많아진 만큼 File I/O 속도가 느린 CSV 포멧이 아닌 HDF 포멧으로 변경하였습니다.


## callAPI.py
def concat_data():
    df_list = list()

    # ./raw_data/dust 아래의 모든 파일을 읽습니다.
    for root, dirs, files in os.walk("./raw_data/dust", topdown=False):
        for name in files:
            df_list.append(pd.read_csv(os.path.join(root, name)))

    dust = pd.DataFrame(pd.concat(df_list, sort=False))

    # Datetime 형태로 Index를 변경해줍니다.
    dust["MSRDT"] = dust["MSRDT"].apply(lambda x: dt.datetime.strptime(str(x), "%Y%m%d%H%M"))
    dust = dust.set_index("MSRDT")

    df_list.clear()

    # ./raw_data/weather 아래의 모든 파일을 읽습니다.
    for root, dirs, files in os.walk("./raw_data/weather", topdown=False):
        for name in files:
            df_list.append(pd.read_csv(os.path.join(root, name)))
    weather = pd.DataFrame(pd.concat(df_list, sort=False))

    # Datetime 형태로 Index를 변경해줍니다.
    weather["TM"] = weather["TM"].apply(lambda x: dt.datetime.strptime(x, "%Y-%m-%d %H:%M"))
    weather = weather.set_index("TM")

    # join() 함수는 같은 index 끼리의 join을 제공합니다.
    weather.join(dust, how="inner").to_hdf("./raw_data/data.hdf", "master")
    dust.to_hdf("./raw_data/data.hdf", "dust")
    weather.to_hdf("./raw_data/data.hdf", "weather")


제공받은 데이터에서 미세먼지에 해당하는 컬럼은 PM10 또는 PM25가 있습니다.

각각 10μm, 2.5μm 이하의 먼지의 농도라고 하는데 그 중에서 초미세먼지 농도에 해당하는 PM25를 사용하고자 한다.

데이터의 분포를 확인하고자 Box Plot을 그려서 확인하고자 한다.

이 때 지역별로 확인해 보는 것이 눈에 더 잘 보일거 같아서 확인해 보았는데..




아니 저 수치가 정말 나올 수 있는 수치인가요..??

서대문구는 무슨 일이 있었던 걸까요..??

노이즈라고 생각이 드는데 다음 포스팅에서 어느 정도 전처리를 하고 사용해야 할 거 같아요.

2021.01.06 : 기상청에서 관리하던 데이터를 공공데이터포털에서 관리하는 걸로 변경이 되었습니다.


긴 글 읽어 주셔서 감사합니다.

공감 버튼은 작성자에게 큰 동기부여가 됩니다.


,