[QGIS + Python] 서울 생활인구 이동 데이터 탐색
누가 어딜 얼만큼 방문하는가.
누가 방문하는 곳에 NDTP의 비율이 높은가.
일단, 데이터부터 물어오자.
빅데이터캠퍼스에 가서 50m 단위 데이터를 얻을 수도 있지만, 몇가지 이슈가 있다.
1. 50m 단위의 이동 데이터가 생각보다 정확하지 않다는 점
2. 연령대, 시간대, 방문목적 별로 분류가 되어있지만, 쟤들을 한번에 엮은 table은 없다는 점 (그러니까, 방문목적에 따른 시간대별, 연령대별 시간대별 등이 없다)
3. 종로구 데이터로 이것저것 해봤는데 별 의미가 없더라
4. 내 연구의 공간적 단위를 50m로 하는 게 의미가 있나? 없을 수도 있겠다.
5. 가기 귀찮다.
그렇다면, 대체할 수 있는 데이터를 서울 열린데이터 광장에서 데이터를 물어와보자.
열린데이터광장 메인
데이터분류,데이터검색,데이터활용
data.seoul.go.kr
가장 작은 단위는 행정동인가. 섭섭하다.
하지만 행정동이 가장 활용할 수 있는 데이터 (상권, 거주인구, 경제적 수준) 가 많겠지.
행정동이 내 연구에서 적합한 단위인가? -- 이건 데이터를 살펴보면서 무엇이 적합한지 확인해보는 게 맞는 것 같다.
일단 데이터 설명서의 핵심적인 내용들을 보자
나는 2023년 11월을 기준으로 할테고, 데이터를 내려받아보면
위 데이터의 시간 단위는 도착시간을 기준으로 작성되어있고, 나는 17시부터 24시까지 데이터를 사용해보기로 했다. (18시를 쓸지, 17시를 쓸지는 모르겠지만, 일단 17시로 해놓고 나중에 자료를 제외하는 게 더 낫다).
요일은? 주중, 주말 다 써도 될 것 같다. 시간이 오후 18시부터니까.
아니, 아니면 주중은 18시부터 24시로 하고, 주중은 전부 쓰는 거 어때? 어차피 이동 유형이 있잖아?
일리가 있어. 하지만 Third Place 에 학원이랑 헬스클럽도 있잖아? 그건 주말에 쉴텐데, 이걸 넣는 건 좀 위험해.
도착시간은? 다 써.
출발 행정동 코드는? 혹시 이게 나중에 경제적 요인으로 사용될 수 있을까? 일단 살리자.
성별, 나이는? 다 쓴다
이동 유형은? 끝이 E로 끝나는 녀석들만 살린다.
평균 이동 시간은 중요하지 않고,
이동인구(합)이 우리의 목표다.
그리고, 1차적으로 찍어보고 싶은 건, "나이" 다.
아무래도 Digital platform 의 사용 여부와 연관성을 보고 싶으니, 나이대별 핫스팟과 NDTP, DTP의 비율이 연관이 있는지가 궁금한거야 나는.
나이를 10대부터 70대까지로 나눠서 grouping 해보고, 그걸 QGIS에 얹는 걸 우선 목표로 하자.
개열받는 포인트는 이동인구(합) 탭이 "string" 형태이고, Null 대신 "*"를 넣어놓았다.
이 진짜 이...
###################################원하는 시간대가 아닌 데이터는 싹 지웠다
filenames = glob('../Data/생활이동_행정동_202311/*.csv')
dfs = []
for filename in filenames:
df = pd.read_csv(filename, encoding = 'cp949')
dfs.append(df)
df_move = pd.concat(dfs)
df_move.head(3)
###################################원하는 대상이 아닌 column 과 row는 싹 지웠다
df_move2 = df_move[['대상연월', '도착시간', '출발 행정동 코드', '도착 행정동 코드', '성별', '나이', '이동유형', '이동인구(합)']]
moving_patterns = ['HE', 'WE', 'EE']
df_move3 = df_move2[(df_move2['이동유형'].isin(moving_patterns)) & (df_move2['이동인구(합)'] != '*')] #오직 entertain 목적의 이동만 보고, "*"로 기록되지 않은 데이터는 날린다
df_move3['이동인구(합)'] = df_move3['이동인구(합)'].astype(float) #float 형태로 보자
print(len(df_move3))
###################################무식해보인다 하지 말라. 가장 쉽고 빠르다.
df_10 = df_move3[(df_move3['나이'] == 10) | (df_move3['나이'] == 15)]
df_20 = df_move3[(df_move3['나이'] == 20) | (df_move3['나이'] == 25)]
df_30 = df_move3[(df_move3['나이'] == 30) | (df_move3['나이'] == 35)]
df_40 = df_move3[(df_move3['나이'] == 40) | (df_move3['나이'] == 45)]
df_50 = df_move3[(df_move3['나이'] == 50) | (df_move3['나이'] == 55)]
df_60 = df_move3[(df_move3['나이'] == 60) | (df_move3['나이'] == 65)]
df_70 = df_move3[(df_move3['나이'] >= 70)]
df_10_sum = df_10[['도착 행정동 코드', '이동인구(합)']].groupby(['도착 행정동 코드']).sum().reset_index().rename(columns = {'도착 행정동 코드': 'hjd_cd', '이동인구(합)': '10s_inflow'})
df_20_sum = df_20[['도착 행정동 코드', '이동인구(합)']].groupby(['도착 행정동 코드']).sum().reset_index().rename(columns = {'도착 행정동 코드': 'hjd_cd', '이동인구(합)': '20s_inflow'})
df_30_sum = df_30[['도착 행정동 코드', '이동인구(합)']].groupby(['도착 행정동 코드']).sum().reset_index().rename(columns = {'도착 행정동 코드': 'hjd_cd', '이동인구(합)': '30s_inflow'})
df_40_sum = df_40[['도착 행정동 코드', '이동인구(합)']].groupby(['도착 행정동 코드']).sum().reset_index().rename(columns = {'도착 행정동 코드': 'hjd_cd', '이동인구(합)': '40s_inflow'})
df_50_sum = df_50[['도착 행정동 코드', '이동인구(합)']].groupby(['도착 행정동 코드']).sum().reset_index().rename(columns = {'도착 행정동 코드': 'hjd_cd', '이동인구(합)': '50s_inflow'})
df_60_sum = df_60[['도착 행정동 코드', '이동인구(합)']].groupby(['도착 행정동 코드']).sum().reset_index().rename(columns = {'도착 행정동 코드': 'hjd_cd', '이동인구(합)': '60s_inflow'})
df_70_sum = df_70[['도착 행정동 코드', '이동인구(합)']].groupby(['도착 행정동 코드']).sum().reset_index().rename(columns = {'도착 행정동 코드': 'hjd_cd', '이동인구(합)': '70s_inflow'})
df_inflows = df_10_sum.merge(df_20_sum, on = 'hjd_cd').merge(df_30_sum, on = 'hjd_cd').merge(df_40_sum, on = 'hjd_cd').merge(df_50_sum, on = 'hjd_cd').merge(df_60_sum, on = 'hjd_cd').merge(df_70_sum, on = 'hjd_cd')
################################## 비율도 야무지게 추가해준다
df_inflows['total'] = df_inflows['10s_inflow'] + df_inflows['20s_inflow'] + df_inflows['30s_inflow'] + df_inflows['40s_inflow'] + df_inflows['50s_inflow'] + df_inflows['60s_inflow'] + df_inflows['70s_inflow']
df_inflows['10s_r'] = df_inflows['10s_inflow']/df_inflows['total']
df_inflows['20s_r'] = df_inflows['20s_inflow']/df_inflows['total']
df_inflows['30s_r'] = df_inflows['30s_inflow']/df_inflows['total']
df_inflows['40s_r'] = df_inflows['40s_inflow']/df_inflows['total']
df_inflows['50s_r'] = df_inflows['50s_inflow']/df_inflows['total']
df_inflows['60s_r'] = df_inflows['60s_inflow']/df_inflows['total']
df_inflows['70s_r'] = df_inflows['70s_inflow']/df_inflows['total']
그리고 QGIS에서
Ctrl + Shift + T 로 csv레이어를 추가해준 뒤
행정동을 중심으로 join 해준다.
그런데 또 개열받는 포인트는 이번에 행정동이 int여서 안붙으니까, str으로 바꿔줘야 한다는 거다.
근데 왜 안붙을까...
행정동 코드가 다르니까 안붙지...
제발 코드 통일 좀 해라 나쁜놈들아 하 진짜 한두번도 아니고
브이월드
국가가 보유하고 있는 공개 가능한 공간정보를 모든 국민이 자유롭게 활용할 수 있도록 다양한 방법을 제공합니다.
www.vworld.kr
여기에서 행정동 데이터를 다시 다운받아서 온다..
안맞는다...
왜 또 이놈의 shp에서는 끝에 0을 하나 더 붙여가지고 말이야...
우리가 가진 행정동 코드와 일치시켜주기 위해 새로운 필드를 만들어준다.
드디어 붙였다...흑흑 저 동들은 왜 안붙었을까...
알게 뭐야... 아니야 이따가 확인해보자...
(--> 확인해보니, 이유는 모르겠지만 아예 생활이동 데이터 집계에서 빠져있다. 다른 코드를 쓰는지 뭔지...)
이게 방문 인구가 많은 동을 20%씩 끊은 quantile 결과다