카이로의 The Functional Art

ref

Cairo, A. Functional Art, The: An Introduction to Information Graphics and Visualization, New Riders, 2012. San Francisco, US.

- ((일반적생각, 카이로의 생각), 제 생각)으로 나누어서 설명

Presentation vs Exploration

Presentation

- 프리젠테이션방식의 시각화는 화자가 다듬은 이야기를 전달하기에 좋은 시각화이다. 즉 잘 정리된 메시지를 전달하기에 좋다.

Exploration

  • 문학적유기체라는 작품이다.
  • 어떤 소설책을 시각화.
  • 수형도 + 칼라
  • 수형도의 의미: 단원, 문단, 문장, 단어 (수형도 계층적 구조를 시각화 하기에 뛰어남. ex: 리그레션트리!)
  • 색깔: 여행, 음악, 파티 등 소설에서 자주 등장하는 소재 (색은 범주형 변수를 표현하기에 뛰어남)

- 익스플로래이션 방식은 독자가 스스로 그림에서 메시지를 찾아낸다.

- 소설을 읽어보지 않은 사람: 이 그래픽으로 소설책의 전체 주제를 미리 파악가능

- 소설을 이미 읽어본 사람: 분석 & 탐구를 할 수 있음. ex: 파티와 음악이 동시에 등장하는 경우가 많다.

절충

- 카이로: 사실 프리젠테이션과 익스플로레이션은 절충가능함

  • aes(x='GDP',y='불평등',text='년도',color='정부')
  • 초록색정부: 소득이 증가 & 불평등이 훨씬 더 증가
  • 갈색정부: 매우 빠른 경제 성장
  • 포인트간의 간격이 조밀하다 = 변화가 더디다 // 포인트간의 간격이 넓다 = 변화가 빠르다.

- 언뜻보기에는 우리에게 익숙한 라인플랏인듯 보이지만 의외로 정보를 해석할만한 요소가 있다.

익스플로레이션형의 그래프는 그릴줄도 알아야 하지만 남이 그린 그래프를 해석할 수도 있어야함.


메시지를 뒷받침하는 그림을 그려야 한다.

인구문제

- 주장1: 가난한 나라에서 애를 너무 많이 낳음 $\to$ 세계인구가 90억까지 증가할 것이다. (현재 70억)

- 주장2: 잘사는 나라에서는 애를 적게 낳음 $\to$ 고령화 문제

리들리의 메시지

- 둘다 틀렸다.

- 가난한 나라의 출산율은 점점 감소

- 잘사는 나라의 출산율은 점점 증가

- 결국 세계의 인구는 안정화 될 것 (증가하지도 감소하지도 않는다)

- 아래의 그림이 그 증거이다.

카이로

- 리들러의 메시지는 아래의 그림들이 더 잘 전달한다.

소감

- 어떠한 현상을 살펴볼때 그것의 부분집합들이 역시 그러한지 살펴보는것은 기본임

- 중요한 선을 제외한 나머지는 회색처리(일러스트레이터 사용) 한 것이 시각적으로 우수하며, 인상적이었음

- 과학적인 논문작업에 들어갈 그림이라면 임의로 회색처리한 것이 다소 비판을 받을 수 있음.


흥미로운 자료와 틀린해석

  • Left: aes(x='출산율', y='수입')
  • Right: aes(x='출산율', y='전체 중학생중 여학생이 차지하는 비율')

- 해설: 당신이 부자가 될수록 당신은 더 적은 아이를 가질 것이다. 중학교에 진학하는 여성이 적을수록 아이를 많이 낳는다. (그림밑에 주석을 해석)

- 여학생들을 중학교에 보내지 않으면 출산율이 올라가나요???


예쁜그림을 그리려다가 메시지가 흐려지지 않도록 주의하라 (1)

- 아래는 남미국가들의 국방력을 시각화한 그림

  • 쓸모없는 그래픽
  • 뭐 기억나는 것이 있나요?

- 아래가 더 우수한 그림이다. 더 정확한 비교를 할 수 있어요.

- 그리고 위의 그림보다 아래의 그림이 더 우수한 시각화이다.

  • 일단 하이라이팅이 우아하네요.. (제가 시험문제 잘못낸 것 같아요)
  • 브라질이 국방력도 우수하고 예산도 많이 투자하는 것 같지만 인구가 흑막인것 같다.

- 흑막을 제거

- 최종적으로 제안하는 그래프

  • 좌측하단: aes(x='인구', y='군인수', size='예산')
  • 우측하단: 관심있는 그래프가 아님

사실 저는 좌측하단의 그래프가 좋은 시각화라고 생각안해요

- 1사분면의 의미: 인구도 높고 군인수도 많은 나라 (똑같은 정보임 의미가 없다. 마치 x축이 토익점수, y축이 텝스점수 같은느낌임)

  • 모든 점들이 직선에 몰려있다면? $\to$ 왜 2차원으로 표현함?

- 저같으면 aes(x='예산(인구효과제거)', y='군인수(인구효과제거)',size='인구')로 할것 같아요.

  • 1사분면의 의미: 예산도 많이 쓰고 군인수도 많은나라 = 콜롬비아.
  • 4사분면의 의미: 예산은 많이 쓰는데 군인수가 적은나라 = 브라질

- 산점도에서 데이터를 한눈에 파악하고 특징을 요약하기 위해서는 X,Y를 너무 비슷한 성질의 변수로 설정하지마라.

아래중 어떤것이 더 바람직한 그래프인가?

  • aes(x='토익', y='텝스', color='합/불', shape='회사의종류')
  • aes(x='토익', y='GPA', color='합/불', shape='회사의종류')

예쁜그림을 그리려다가 메시지가 흐려지지 않도록 주의하라 (2)

  • 명암으로 왜 크기비교를 하는것인가?

- 비교를 위해서는 바플랏이 더 우수하다.


예쁜그림을 그리려다가 메시지가 흐려지지 않도록 주의하라 (3): 버블의 유행

- 카이로교수님의 강의자료에 등장하는 그림

- 회색이 befor, 검은색이 after

  • 크기비교는 바플랏으로 하는것이 아니다.

- 우리눈은 작원원이 큰원의 절반정도 차지한다고 느껴진다.

- 그렇지만 실제로는 아래와 같음

- 버블차트는 크기를 왜곡시킨다.


- 하지만 아래의 버블차트는 우수하다.

- 선거지도는 수치비교에 별로 관심이 없다.

- 대신에 민주당표와 공화당표가 어떤 지역에 몰렸는지 파악하는 것이중요

- 따라서 aes중 가장 중요한 x,y를 모두 지역에 투자함


라인플랏, 바플랏, 산점도

- 시간경과에 따른 변화를 보여주고 싶으면 라인플랏, 비교를 하고 싶다면 바플랏, 관계를 알고싶다면 산점도.

데이터 프레임 합치기

import

import pandas as pd 

data

df2016=pd.read_csv("https://raw.githubusercontent.com/PacktPublishing/Pandas-Cookbook/master/data/stocks_2016.csv")
df2017=pd.read_csv("https://raw.githubusercontent.com/PacktPublishing/Pandas-Cookbook/master/data/stocks_2017.csv")
df2018=pd.read_csv("https://raw.githubusercontent.com/PacktPublishing/Pandas-Cookbook/master/data/stocks_2018.csv")

pd.concat([...])

예제1

df2016
Symbol Shares Low High
0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
df2017
Symbol Shares Low High
0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
df2018
Symbol Shares Low High
0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400
pd.concat([df2016,df2017,df2018]) 
Symbol Shares Low High
0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400
pd.concat([df2016,df2017,df2018]).reset_index()
index Symbol Shares Low High
0 0 AAPL 80 95 110
1 1 TSLA 50 80 130
2 2 WMT 40 55 70
3 0 AAPL 50 120 140
4 1 GE 100 30 40
5 2 IBM 87 75 95
6 3 SLB 20 55 85
7 4 TXN 500 15 23
8 5 TSLA 100 100 300
9 0 AAPL 40 135 170
10 1 AMZN 8 900 1125
11 2 TSLA 50 220 400
pd.concat([df2016,df2017,df2018]).reset_index(drop=True)
Symbol Shares Low High
0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
3 AAPL 50 120 140
4 GE 100 30 40
5 IBM 87 75 95
6 SLB 20 55 85
7 TXN 500 15 23
8 TSLA 100 100 300
9 AAPL 40 135 170
10 AMZN 8 900 1125
11 TSLA 50 220 400

- R에서 rbind와 유사하다

예제2

- rbind와의 차이점: 합치려는 데이터 프레임의 columns이 꼭 동일할 필요는 없다.

df2016
Symbol Shares Low High
0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
df2017.iloc[:,1:]
Shares Low High
0 50 120 140
1 100 30 40
2 87 75 95
3 20 55 85
4 500 15 23
5 100 100 300
pd.concat([df2016,df2017.iloc[:,1:]])
Symbol Shares Low High
0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
0 NaN 50 120 140
1 NaN 100 30 40
2 NaN 87 75 95
3 NaN 20 55 85
4 NaN 500 15 23
5 NaN 100 100 300

pd.concat([...],axis='columns')

- R에서 cbind와 비슷한 느낌이다. (그런데 row의 숫자가 서로 달라도 괜찮음)

예제3

df2016
Symbol Shares Low High
0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
df2017
Symbol Shares Low High
0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
df2018
Symbol Shares Low High
0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400
pd.concat([df2016,df2017,df2018],axis='columns')
Symbol Shares Low High Symbol Shares Low High Symbol Shares Low High
0 AAPL 80.0 95.0 110.0 AAPL 50 120 140 AAPL 40.0 135.0 170.0
1 TSLA 50.0 80.0 130.0 GE 100 30 40 AMZN 8.0 900.0 1125.0
2 WMT 40.0 55.0 70.0 IBM 87 75 95 TSLA 50.0 220.0 400.0
3 NaN NaN NaN NaN SLB 20 55 85 NaN NaN NaN NaN
4 NaN NaN NaN NaN TXN 500 15 23 NaN NaN NaN NaN
5 NaN NaN NaN NaN TSLA 100 100 300 NaN NaN NaN NaN
pd.concat([df2016,df2017,df2018],axis=1)
Symbol Shares Low High Symbol Shares Low High Symbol Shares Low High
0 AAPL 80.0 95.0 110.0 AAPL 50 120 140 AAPL 40.0 135.0 170.0
1 TSLA 50.0 80.0 130.0 GE 100 30 40 AMZN 8.0 900.0 1125.0
2 WMT 40.0 55.0 70.0 IBM 87 75 95 TSLA 50.0 220.0 400.0
3 NaN NaN NaN NaN SLB 20 55 85 NaN NaN NaN NaN
4 NaN NaN NaN NaN TXN 500 15 23 NaN NaN NaN NaN
5 NaN NaN NaN NaN TSLA 100 100 300 NaN NaN NaN NaN
  • 칼럼이 길잖아요.. 그래서 1이에요..

pd.concat([...],keys=[...])

예제4

df2016
Symbol Shares Low High
0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
df2017
Symbol Shares Low High
0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
df2018
Symbol Shares Low High
0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400
pd.concat([df2016,df2017,df2018],keys=[2016,2017,2018])
Symbol Shares Low High
2016 0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
2017 0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
2018 0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400

- 위의 코드는 아래와 같다.

pd.concat({2016:df2016,2017:df2017,2018:df2018})
Symbol Shares Low High
2016 0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
2017 0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
2018 0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400

- 그런데 {2016:df2016,2017:df2017,2018:df2018}dict(zip([2016,2017,2018], [df2016,df2017,df2018])) 는 같으므로 위의코드는 다시 아래와 같다.

pd.concat(dict(zip([2016,2017,2018],[df2016,df2017,df2018])))
Symbol Shares Low High
2016 0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
2017 0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
2018 0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400

- 의도하지 않은 코드들

pd.concat([df2016,df2017,df2018],keys=[2016,2017])
Symbol Shares Low High
2016 0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
2017 0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
pd.concat([df2016,df2017,df2018],keys=[2016,2018])
Symbol Shares Low High
2016 0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
2018 0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
pd.concat([df2016,df2017,df2018],keys=[2016,2017,2018,2019])
Symbol Shares Low High
2016 0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
2017 0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
2018 0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400

pd.concat([...],keys=[...],axis='columns')

예제5

df2016
Symbol Shares Low High
0 AAPL 80 95 110
1 TSLA 50 80 130
2 WMT 40 55 70
df2017
Symbol Shares Low High
0 AAPL 50 120 140
1 GE 100 30 40
2 IBM 87 75 95
3 SLB 20 55 85
4 TXN 500 15 23
5 TSLA 100 100 300
df2018
Symbol Shares Low High
0 AAPL 40 135 170
1 AMZN 8 900 1125
2 TSLA 50 220 400
pd.concat([df2016,df2017,df2018],axis=1)
Symbol Shares Low High Symbol Shares Low High Symbol Shares Low High
0 AAPL 80.0 95.0 110.0 AAPL 50 120 140 AAPL 40.0 135.0 170.0
1 TSLA 50.0 80.0 130.0 GE 100 30 40 AMZN 8.0 900.0 1125.0
2 WMT 40.0 55.0 70.0 IBM 87 75 95 TSLA 50.0 220.0 400.0
3 NaN NaN NaN NaN SLB 20 55 85 NaN NaN NaN NaN
4 NaN NaN NaN NaN TXN 500 15 23 NaN NaN NaN NaN
5 NaN NaN NaN NaN TSLA 100 100 300 NaN NaN NaN NaN
pd.concat([df2016,df2017,df2018],axis=1,keys=[2016,2017,2018])
2016 2017 2018
Symbol Shares Low High Symbol Shares Low High Symbol Shares Low High
0 AAPL 80.0 95.0 110.0 AAPL 50 120 140 AAPL 40.0 135.0 170.0
1 TSLA 50.0 80.0 130.0 GE 100 30 40 AMZN 8.0 900.0 1125.0
2 WMT 40.0 55.0 70.0 IBM 87 75 95 TSLA 50.0 220.0 400.0
3 NaN NaN NaN NaN SLB 20 55 85 NaN NaN NaN NaN
4 NaN NaN NaN NaN TXN 500 15 23 NaN NaN NaN NaN
5 NaN NaN NaN NaN TSLA 100 100 300 NaN NaN NaN NaN
pd.concat({2016:df2016,2017:df2017,2018:df2018},axis=1)
2016 2017 2018
Symbol Shares Low High Symbol Shares Low High Symbol Shares Low High
0 AAPL 80.0 95.0 110.0 AAPL 50 120 140 AAPL 40.0 135.0 170.0
1 TSLA 50.0 80.0 130.0 GE 100 30 40 AMZN 8.0 900.0 1125.0
2 WMT 40.0 55.0 70.0 IBM 87 75 95 TSLA 50.0 220.0 400.0
3 NaN NaN NaN NaN SLB 20 55 85 NaN NaN NaN NaN
4 NaN NaN NaN NaN TXN 500 15 23 NaN NaN NaN NaN
5 NaN NaN NaN NaN TSLA 100 100 300 NaN NaN NaN NaN
pd.concat(dict(zip([2016,2017,2018],[df2016,df2017,df2018])),axis=1)
2016 2017 2018
Symbol Shares Low High Symbol Shares Low High Symbol Shares Low High
0 AAPL 80.0 95.0 110.0 AAPL 50 120 140 AAPL 40.0 135.0 170.0
1 TSLA 50.0 80.0 130.0 GE 100 30 40 AMZN 8.0 900.0 1125.0
2 WMT 40.0 55.0 70.0 IBM 87 75 95 TSLA 50.0 220.0 400.0
3 NaN NaN NaN NaN SLB 20 55 85 NaN NaN NaN NaN
4 NaN NaN NaN NaN TXN 500 15 23 NaN NaN NaN NaN
5 NaN NaN NaN NaN TSLA 100 100 300 NaN NaN NaN NaN

Folium

import

import numpy as np 
import pandas as pd
import folium

folium.Map

- global view

m=folium.Map(scrollWheelZoom=False)
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

- location과 scale을 조정하는 방법

m=folium.Map(scrollWheelZoom=False,
             location=[35.84676618432067, 127.12936405235232],
             zoom_start=16
            )
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

- tiles 옵션을 주어서 지도의 외형을 변경하여 보자.

  • "OpenStreetMap"
  • "Stamen Terrain", "Stamen Toner", "Stamen Watercolor"
  • "CartoDB positron", "CartoDB dark_matter"
m=folium.Map(scrollWheelZoom=False,
             location=[35.84676618432067, 127.12936405235232],
             zoom_start=15,
             tiles="CartoDB positron"
            )
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

folium.Marker

- 지도위에 무엇인가 오브젝트를 추가하는데, 가장 기본적인 것이 마커임 (점이라 생각해도 괜찮음)

- 마커안에 내용을 넣을 수 있음. (경우에 따라서는 유용하다)

m=folium.Map(scrollWheelZoom=False,
             location=[35.84676618432067, 127.12936405235232],
             zoom_start=14,
             tiles="CartoDB positron"
            )
folium.Marker(location=[35.84676618432067, 127.12936405235232],
              #tooltip='Click me',
              popup='JBNU',
              icon=folium.Icon(color='red',icon='university',prefix='fa')
             ).add_to(m) 

folium.Marker(location=[35.837688889992634, 127.11212714586104],
              #tooltip='Click me',
              popup='HOME'              
             ).add_to(m) 
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

- 조금 더 다양한 아이콘들을 추가하여 보자.

m=folium.Map(scrollWheelZoom=False,
             location=[35.84676618432067, 127.12936405235232],
             zoom_start=14,
             tiles="CartoDB positron"
            )
folium.Marker(location=[35.84676618432067, 127.12936405235232],
              #tooltip='Click me',
              popup='JBNU',
              icon=folium.Icon(color='red',icon='music',prefix='fa')
             ).add_to(m) 

folium.Marker(location=[35.837688889992634, 127.11212714586104],
              #tooltip='Click me',
              popup='HOME'              
             ).add_to(m) 
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

- icon='university' 대신에 쓸만한 옵션들

  • icon='street-view'
  • icon='tree'
  • icon='plane'
  • icon='bell'
  • ...

https://getbootstrap.com/docs/3.3/components/

- 마커에 HTML넣기

m=folium.Map(scrollWheelZoom=False,
             location=[35.84676618432067, 127.12936405235232],
             zoom_start=14,
             tiles="CartoDB positron"
            )
folium.Marker(location=[35.84676618432067, 127.12936405235232],
              #tooltip='Click me',
              popup='<h2> JBNU </h2><br>',
              icon=folium.Icon(color='red',icon='music',prefix='fa')
             ).add_to(m) 

folium.Marker(location=[35.837688889992634, 127.11212714586104],
              #tooltip='Click me',
              popup='<h5> HOME </h5><br>'              
             ).add_to(m) 
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

- 마커에 DataFrame (을 HTML로 바꿔서) 넣기

df= pd.DataFrame(data=[[2019,35],[2020,35],[2021,33]],columns=['Year','Enrollment'])
df.to_html()
'<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>Year</th>\n      <th>Enrollment</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0</th>\n      <td>2019</td>\n      <td>35</td>\n    </tr>\n    <tr>\n      <th>1</th>\n      <td>2020</td>\n      <td>35</td>\n    </tr>\n    <tr>\n      <th>2</th>\n      <td>2021</td>\n      <td>33</td>\n    </tr>\n  </tbody>\n</table>'
m=folium.Map(scrollWheelZoom=False,
             location=[35.84676618432067, 127.12936405235232],
             zoom_start=14,
             tiles="CartoDB positron"
            )
folium.Marker(location=[35.84676618432067, 127.12936405235232],
              #tooltip='Click me',
              popup='<h2> JBNU </h2><br>'+df.to_html(),
              icon=folium.Icon(color='red',icon='music',prefix='fa')
             ).add_to(m) 

folium.Marker(location=[35.837688889992634, 127.11212714586104],
              #tooltip='Click me',
              popup='<h5> HOME </h5><br>'              
             ).add_to(m) 
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

- 논리구조상 HTML 오브젝트를 아무거나 넣을 수 있다. $\to$ 그림도 넣을 수 있나?

import base64
import matplotlib.pyplot as plt 
fig = plt.figure()
plt.plot([1,2,3,4],[2,3,4,3],'o--r')
fig.savefig('temp.png')
_encoded = base64.b64encode(open('temp.png','rb').read())
_myhtml = '<img src="data:image/png;base64,{}">'.format
_iframe = folium.IFrame(_myhtml(_encoded.decode('UTF-8')),width=400,height=300)
_popup = folium.Popup(_iframe)
  • 인터넷에서 긁어온 코드
m=folium.Map(scrollWheelZoom=False,
             location=[35.84676618432067, 127.12936405235232],
             zoom_start=13,
             tiles="CartoDB positron"
            )
folium.Marker(location=[35.84676618432067, 127.12936405235232],
              #tooltip='Click me',
              popup=_popup,
              icon=folium.Icon(color='red',icon='music',prefix='fa')
             ).add_to(m) 

folium.Marker(location=[35.837688889992634, 127.11212714586104],
              #tooltip='Click me',
              popup='<h5> HOME </h5><br>'              
             ).add_to(m) 
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

folium.CircleMarker

m=folium.Map(scrollWheelZoom=False,
             location=[35.84676618432067, 127.12936405235232],
             zoom_start=13,
             tiles="CartoDB positron"
            )
folium.Marker(location=[35.84676618432067, 127.12936405235232],
              #tooltip='Click me',
              popup=_popup,
              icon=folium.Icon(color='red',icon='music',prefix='fa')
             ).add_to(m) 

folium.CircleMarker(location=[35.84676618432067, 127.12936405235232], 
                    radius=40,
                    color=None,
                    fill=True,
                    fill_color='red',
                    popup='JBNU').add_to(m)

folium.Marker(location=[35.837688889992634, 127.11212714586104],
              #tooltip='Click me',
              popup='<h5> HOME </h5><br>'              
             ).add_to(m) 
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

- 다양한 마커를 그냥 지옴처럼 생각해도 괜찮아보임

- 동그라미 하나 그리는데 너무 많이 노력을 하는것 같기도 함.

- Circle이 아니라 라인, 사각형, 폴리곤등도 지도에 추가할수 있으나 더 많은 노력이 필요하다.

plugins.HeatMap

- Heatmap은 폴리움에서 데이터 시각화를 하기에 적합한 기본도구임

from folium import plugins 
data=(np.random.normal(size=(100,3)) + np.array([[28,77,5]])).tolist()
m=folium.Map([28,77],zoom_start=6,scrollWheelZoom=False)
plugins.HeatMap(data).add_to(m) 
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

plugins.HeatMapWithTime

- 입자의 위치

$$\begin{cases} x_t = x_{t-1} +\epsilon_t \\ y_t = y_{t-1} +\eta_t \end{cases}$$

단 $(x_0,y_0)=(35.837688889992634, 127.11212714586104)$ 라고 하고 $\epsilon_t$와 $\eta_t$는 서로 독립인 정규분포이다.

  • 최종적인 차원: 프레임수 점의수 2

step1: numpy

t0 = np.array([[35.837688889992634,127.11212714586104]]*3000)
walk = np.random.normal(size=(200,3000,2)) * 0.0001 
t0 # 시점0 
array([[ 35.83768889, 127.11212715],
       [ 35.83768889, 127.11212715],
       [ 35.83768889, 127.11212715],
       ...,
       [ 35.83768889, 127.11212715],
       [ 35.83768889, 127.11212715],
       [ 35.83768889, 127.11212715]])
t0+walk[0,:,:]# 시점1
array([[ 35.83777344, 127.11205323],
       [ 35.83791409, 127.11202349],
       [ 35.83767529, 127.11216554],
       ...,
       [ 35.83760035, 127.11213879],
       [ 35.83762557, 127.11184101],
       [ 35.83768212, 127.11211675]])
t0+walk[0,:,:]+walk[1,:,:]# 시점2
array([[ 35.8376115 , 127.11182984],
       [ 35.83787414, 127.11188578],
       [ 35.83762354, 127.11217885],
       ...,
       [ 35.83773468, 127.11226384],
       [ 35.83769101, 127.11176839],
       [ 35.83772116, 127.1120796 ]])

- 그런데 walk[0,:,:]+walk[1,:,:]를 좀더 간단하게 쓸수 있다.

walk[0,:,:]+walk[1,:,:]
array([[-7.73876188e-05, -2.97310517e-04],
       [ 1.85249266e-04, -2.41366853e-04],
       [-6.53513070e-05,  5.17036596e-05],
       ...,
       [ 4.57886787e-05,  1.36693732e-04],
       [ 2.11504353e-06, -3.58757408e-04],
       [ 3.22726535e-05, -4.75494884e-05]])
walk[:2,:,:].sum(axis=0)
array([[-7.73876188e-05, -2.97310517e-04],
       [ 1.85249266e-04, -2.41366853e-04],
       [-6.53513070e-05,  5.17036596e-05],
       ...,
       [ 4.57886787e-05,  1.36693732e-04],
       [ 2.11504353e-06, -3.58757408e-04],
       [ 3.22726535e-05, -4.75494884e-05]])

- 따라서 시점2는 아래와 같이 표현가능

t0+walk[:2,:,:].sum(axis=0)
array([[ 35.8376115 , 127.11182984],
       [ 35.83787414, 127.11188578],
       [ 35.83762354, 127.11217885],
       ...,
       [ 35.83773468, 127.11226384],
       [ 35.83769101, 127.11176839],
       [ 35.83772116, 127.1120796 ]])

- 그렇다면 시점3은?

t0+walk[:3,:,:].sum(axis=0)
array([[ 35.83758403, 127.11180249],
       [ 35.83785318, 127.11200554],
       [ 35.83759465, 127.11226819],
       ...,
       [ 35.83785369, 127.11223186],
       [ 35.83772452, 127.11194016],
       [ 35.83756599, 127.11217841]])

- 따라서 data 는

data = [(t0+walk[:i,:,:].sum(axis=0)).tolist() for i in range(200)]
np.array(data).shape
(200, 3000, 2)

- 이제 data[0], data[1], data[2], ... data[199]까지 대입하여 각각 그림을 그리고 시각화하면 된다.

step2: folium

m=folium.Map(scrollWheelZoom=False,
             location=[35.837688889992634,127.11212714586104],
             zoom_start=16
            )
plugins.HeatMapWithTime(data,radius=5).add_to(m)
#m
Make this Notebook Trusted to load map: File -> Trust Notebook
  • 가스의 확산과정

예제: earthquakes

Step1: Pandas

df=pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/earthquakes-23k.csv')
df
Date Latitude Longitude Magnitude
0 01/02/1965 19.2460 145.6160 6.0
1 01/04/1965 1.8630 127.3520 5.8
2 01/05/1965 -20.5790 -173.9720 6.2
3 01/08/1965 -59.0760 -23.5570 5.8
4 01/09/1965 11.9380 126.4270 5.8
... ... ... ... ...
23407 12/28/2016 38.3917 -118.8941 5.6
23408 12/28/2016 38.3777 -118.8957 5.5
23409 12/28/2016 36.9179 140.4262 5.9
23410 12/29/2016 -9.0283 118.6639 6.3
23411 12/30/2016 37.3973 141.4103 5.5

23412 rows × 4 columns

lst=[df.assign(Year=list(map(lambda x: x.split('/')[-1], df.Date))).\
groupby('Year').\
pipe(list)[i][1].iloc[:,[1,2,3]].\
to_numpy().tolist() for i in range(2017-1965)]

Step2: folium

m=folium.Map(scrollWheelZoom=False)
plugins.HeatMapWithTime(lst,radius=5,index=list(range(1965,2017))).add_to(m)
#m
Make this Notebook Trusted to load map: File -> Trust Notebook

- 숙제:

지진자료에서 1985년 이후의 자료만 고른뒤에 HeatMapWithTime()을 이용하여 시각화하라.

lst=[df.assign(Year=list(map(lambda x: x.split('/')[-1], df.Date))).\
query('Year > "1984"').groupby('Year').\
pipe(list)[i][1].iloc[:,[1,2,3]].\
to_numpy().tolist() for i in range(2017-1985)]
m=folium.Map(scrollWheelZoom=False)
plugins.HeatMapWithTime(lst,radius=5,index=list(range(1985,2017))).add_to(m)
m
Make this Notebook Trusted to load map: File -> Trust Notebook