Programming Language/Python

데이터 관계분석

D4tai1 2018. 9. 30.

이번 장에서 다루는 내용.

- 서울시 CCTV 수와 인구수의 관계를 분석하려고 한다.

- 그러기 위해서는 구별 CCTV수와 인구수를 알아야한다.

※ Jupyter Notebook을 사용하려고 한다.

※ numpy, pandas, matplotlib를 사용하여 데이터를 편집하고 그래프로 시각화하려고 한다.

 

1. 데이터 다운로드[1]

- 구글에서 위 사진과 같이 검색한다.

 

- 마우스 버튼이 있는 내용보기를 누른다.

 

- csv형식의 파일을 다운로드 받는다.

 

2. 읽어오기[1]

1) pandas와 numpy

import pandas as pd
# pandas를 pd라고 부르겠다.
import numpy as np
# numpy를 np라고 부르겠다.

[1] pandas

 - 데이터 분석 및 처리를 쉽게 할 수 있도록 만들어 놓은 파이썬 패키지이다.

[2] numpy

 - Numerical Python의 약자이며 수치계산을 하기 위해 만들어진 파이썬 패키지이다.

 

2) csv파일 읽어오기

# 위에서 다운로드 받은 서울시 년도별 CCTV현황파일 읽어오기
CCTV_Seoul = pd.read_csv('./CCTV_in_Seoul_2018.csv', encoding='euc-kr')
# .read_csv([파일경로], encoding = [인코딩타입])함수의 형식
CCTV_Seoul.head()
# 상위 5개만 출력

 

3) 컬럼리스트 확인

# CCTV_Seoul은 읽어온 데이터를 저장하는 변수명
# CCTV_in_Seoul_2018.csv에 있는 컬럼명 -> 리스트로 출력
CCTV_Seoul.columns

- 위와 같이 리스트가 출력됨을 확인할 수 있다.

 

4) 1번째 컬럼 확인

# 첫번째 컬럼명 호출
CCTV_Seoul.columns[0]

'기관명'  <- 좌측 텍스트가 첫번째 컬럼명임을 확인할 수 있다.

 

5) 컬럼명 변경

# CCTV_Seoul의 index 0번째 값의 이름을 바꾼다.
# inplace = True는 변수를 직접 변경한다는 뜻이다.
CCTV_Seoul.rename(columns={CCTV_Seoul.columns[0] : '구별'}, inplace = True)
CCTV_Seoul.head()

- 첫번째 컬럼명이 '기관명'에서 '구별'로 변경됨을 확인할 수 있다.

 

3. 데이터 다운로드[2]

- 정제된 데이터를 가져오기 위해 http://data.seoul.go.kr/에 접속한다.

-

- 데이터 이용하기-> 데이터서비스 -> 서울통계 데이터 -> 서울통계 서비스 를 클릭한다.

 

- 인구 -> 주민등록인구(연령별/구별) 에 들어간다.

 

- csv형식으로 다운로드 한다.

4. 읽어오기[2]

1) 엑셀파일 읽어오기

# population_in_Seoul.xls 파일을 읽기
pop_Seoul = pd.read_excel('./population_in_Seoul.xls', encoding = 'euc-kr')
pop_Seoul.head()

- 다운로드한 파일을 pop_Seoul변수에 읽어와 위 그림과 같이 상위 5개만 출력하였다.

 

2) 출력영역 정하기

# population_in_Seoul.xls의 데이터 중, 
# header = 2 옵션은 위에서 3번째줄부터 값들을 저장한다는 의미이다.
# parse_cols='B, D, G, J, N' 옵션은 B, D, G, J, N 열의 값만 저장한다.

pop_Seoul = pd.read_excel('./population_in_Seoul.xls', header=2, parse_cols='B, D, G, J, N', encoding = 'euc-kr')
pop_Seoul.head()

- 3번째 행부터 특정 열만 저장되어 출력된 것을 확인할 수 있다.

 

3) 컬럼명 변경하기

# 위에서 1번 정제과정을 거친 population_in_Seoul.xls의 가로줄 이름을 바꾸고 
# 실제 데이터에서도 적용

pop_Seoul.rename(columns={pop_Seoul.columns[0] : '구별',
                          pop_Seoul.columns[1] : '인구수',
                          pop_Seoul.columns[2] : '한국인',
                          pop_Seoul.columns[3] : '외국인',
                          pop_Seoul.columns[4] : '고령자'}, inplace = True)
pop_Seoul.head()

- 컬럼명이 변경된 것을 확인할 수 있다.

 

4) 정렬하기[1]

- 오름차순

#CCTV_in_Seoul_2018.csv의 맨 위 부터 5줄을 출력

#소계를 중심으로 오름차순
# ascending 옵션의 True는 오름차순, False는 내림차순 정렬이다.
CCTV_Seoul.sort_values(by='소계', ascending=True).head(5)

- 소계를 기준으로 오름차순 정렬되었다.

 

- 내림차순

#소계를 중심으로 내림차순
CCTV_Seoul.sort_values(by='소계', ascending=False).head(5)

- 소계를 기준으로 내림차순 정렬되었다.

 

5) 새로운 컬럼 생성하기[1]

#CCTV의 최근 증가율을 구하기 위해서는 17년+16년+15년+14년을 더해서 13년 이전으로 나누고 100를 곱해준다.
#'최근증가율'을 기준으로 내림차순 정리하여 5째줄까지 출력.

CCTV_Seoul['최근증가율'] = (CCTV_Seoul['2017년']
                       + CCTV_Seoul['2016년'] 
                       + CCTV_Seoul['2015년'] 
                       + CCTV_Seoul['2014년']) / CCTV_Seoul['2013년 이전'] * 100

#최근증가율을 중심으로 내림차순
CCTV_Seoul.sort_values(by='최근증가율', ascending=False).head(5)

- 최근증가율이라는 컬럼이 생성되었고 내용은 13년대비 최근 5년간 CCTV증가비율을 계산하였다.

 

6) 이상치데이터 삭제하기

[1] 이상치데이터란?

 - 측정 데이터 범위를 벗어난 값을 말한다.

# '구별' 컬럼에서 1번이라도 나온 값을 출력
# (중복된 것도 1번만 나옴)
# 서울시에 강서구가 1개만 있어야 한다.

pop_Seoul['구별'].unique()
# 구가 아닌 nan이 들어있다.(이상치데이터)

- 출력결과를 잘 살펴보면 마지막에 nan 이라는 데이터가 들어있고 Enter로 인해 발생한 것으로 예상된다.

 

7) index 찾기

# isnull() : '구별'에서 NAN값이 있는 것을 출력
# 널값이 있는 행 출력!!
pop_Seoul[pop_Seoul['구별'].isnull()]

- 26번째 인덱스임을 확인한다.

 

8) 행 삭제

# 위에서 26번줄에서 Nan이 있는 것을 알아서 삭제하려고 한다.
pop_Seoul.drop([26], inplace=True)
pop_Seoul.tail()

- 아래서부터 5개를 출력해서 26번째 인덱스가 삭제됨을 확인한다.

 

9) 새로운 컬럼 생성하기[2]

# 외국인/고령자 비율을 구하기 위해서 인구수로 나누어주고 100을 곱한다.
# 외국인비율과 고령자비율이라는 필드 2개가 생성되었다.

pop_Seoul['외국인비율'] = pop_Seoul['외국인'] / pop_Seoul['인구수'] * 100
pop_Seoul['고령자비율'] = pop_Seoul['고령자'] / pop_Seoul['인구수'] * 100
pop_Seoul.head()

- 그림에서 보이는 것처럼 외국인비율과 고령자비율 열이 추가되었다.

 

10) 정렬하기[2]

# '인구수'를 기준을 내림차순 정렬 후 출력
pop_Seoul.sort_values(by='인구수', ascending=False).head(10)

- 인구수를 기준으로 내림차순 정렬되었다.

 

11) 파일 내보내기

# pop_Seoul_finalData.xls 저장
pop_Seoul.to_excel('./pop_Seoul_result_save.xls', encoding='euc-kr')
# 저장 후 파일을 열어서 확인해볼 수 있다.

 

12) 병합하기

# CCTV_Seoul과 pop_Seoul의 공동 컬럼인 '구별' 컬럼으로 두 데이터프레임을 합친다.
# 합치고 난 후, 맨 위 5줄을 출력

data_result = pd.merge(CCTV_Seoul, pop_Seoul, on="구별")
data_result.head()

- '구별' 컬럼을 기준으로 두 데이터프레임이 합쳐진 것을 확인할 수 있다.

 

13) 열 삭제하기

# 행 방향으로 데이터 삭제 : drop
# 열 방향으로 데이터 삭제 : del

del data_result['2013년 이전']
del data_result['2014년']
del data_result['2015년']
del data_result['2016년']
del data_result['2017년']
data_result.head()

 - 5개의 열이 삭제된 것을 확인할 수 있다.

 

14) 상관관계 확인하기

# np.corrcoef() : 상관계수를 계산해주는 함수
# 1이면 양의 상관관계 - 1사분면 상의 비례그래프 
# -1이면 음의 상관관계 - 1사분면 상의 반비례그래프
# 0이면 상관 없음

# 고령자비율과 CCTV의 개수을 상관관계 보기
np.corrcoef(data_result['고령자비율'],data_result['소계'])
# 1을 제외한 값을 기준으로 상관관계가 있는지 없는지 확인할 수 있다

- 1을 100%으로 보면 0.25는 25%로 '고령자비율'과 'CCTV개수' 는 상관관계가 크지 않다고 보여진다.

 

# 외국인비율과 CCTV의 개수을 상관관계 보기
np.corrcoef(data_result['외국인비율'],data_result['소계'])

- 0%에 가까운 3%정도 되는 것으로 보아 상관이 거의 없는 것으로 보여진다.

 

5. 그래프로 분석하기

1) 그래프 작성시 사용하는 라이브러리 추가하기

#matplotlib(맷플로라이브) -> 그래프 그릴 때 사용하지만 한글을 지원하지 않기 때문에 폰트를 변경해야 한다.

import platform
import matplotlib.pyplot as plt

%matplotlib inline
#주피터 노트북에서 지원해주는 라이브러리이다.

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')

 

2) 막대그래프 시각화하기

# 'barh' : 수평 막대그래프
# 그래프 뒤 격자무늬 표시하기
# 그래프 사이즈는 10 * 10
# .show()는 보여주다.

data_result['소계'].plot(kind='barh', grid=True, figsize=(10, 10))
plt.show()
# plt는 위에서 import matplotlib.pyplot as plt 로 선언.

- '구별'로 '소계'를 막대그래프로 보여준다.

 

3) 정렬하기[3]

# 데이터 값을 오름차순으로 정렬
# 기본으로 내림차순 정리가 되어있다.
# grid 는 보여주다
data_result['소계'].sort_values().plot(kind='barh', grid=True, figsize=(10, 10))

- '소계'를 기준으로 내림차순 정렬이 되어있다.

 

4) 새로운 컬럼 생성하기[3]

#고령자 / 인구수

data_result['고령자비율'] = data_result['고령자'] / data_result['인구수'] * 100
data_result['고령자비율'].sort_values().plot(kind='barh', grid=True, figsize=(10, 10))

plt.show()

- 한 눈에 봐도 알 수 있듯이 강북구에 인구 수 대비 고령자가 많이 사는 것을 확인할 수 있다.

 

5) 산포도그래프 시각화하기

#그래프 사이즈는 6 * 6
# 산포도 그래프로 시각화
# 점의 크기는 50
# xlabel의 이름은 '인구수'
# ylabel의 이름은 'CCTV'
# 그래프 배경에 격자 무늬 표시

plt.figure(figsize=(6,6))
plt.scatter(data_result['인구수'], data_result['소계'], s=50)
plt.xlabel('인구수')
plt.ylabel('CCTV')
plt.grid()
plt.show()

- x축과 y축으로 구성된 2차원 화면에서 산포도를 확인할 수 있다.

 

6) 직선 그리기[1]

# np.polyfit() : 직선을 만드는 함수이다.
# 직선의 개수는 1개이다
# 독립변수 : 인구수, 종속변수, 소계를 최소 에러값으로 fitting하는 다항식 곡선식을 구한다.
# 참고 URL : https://docs.scipy.org/doc/numpy-1.14.0/reference.gernerated.numpy.polyfit.h...
# 출력결과의 하나는 기울기이고 하나는 절편이다.

fp1 = np.polyfit(data_result['인구수'], data_result['소계'], 1)
fp1
#직선을 긋는 함수

 

7) 수치값 표현하기

# 우리눈에는 직선으로 보이지만 하나하나를 수치값을 표현한다.

# x축 데이터는 np.linspace로 값을 할당하고,
# y축4 데이터는 np.polyld로 값을 할당한다.

f1 = np.poly1d(fp1)
print(f1)

#x축 값 100,000 ~ 700,000 구간을 100개의 구간으로 나눔
fx = np.linspace(100000, 700000, 100)
print(fx)

 

8) 직선그리기[2]

# 직선의 모양(dashed)은 점선이고 선의 두께는 3, 선 색깔은 'green'으로 설정
# 원의 크기는 s = 50

plt.figure(figsize=(10, 10))
plt.scatter(data_result['인구수'], data_result['소계'], s = 50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')
plt.xlabel('인구수')
plt.ylabel('CCTV')
plt.grid()
plt.show()

 

9) 오차 구하기

fp1 = np.polyfit(data_result['인구수'], data_result['소계'], 1)

f1 = np.poly1d(fp1)
fx = np.linspace(100000, 700000, 100)

# 오차를 구할 때, '소계'-'인구수'를 한다.
# 이 때, 음수로 결과가 나오지 않도록 np.abs 함수를 사용해 절대값으로 출력한다.

data_result['오차'] = np.abs(data_result['소계'] - f1(data_result['인구수']))

# '오차'로 내림차순 정리
df_sort = data_result.sort_values(by='오차', ascending=False)
df_sort.head()

 10) 오차범위 적용해서 그래프 시각화하기

# 그래프의 크기는 14 * 10
# 산포도 그래프의 x값은 '인구수', y값은 '소계', '오차' 데이터 표시는 데이터의 값에 따라
# 색이 변한다.

plt.figure(figsize=(14, 10))
plt.scatter(data_result['인구수'], data_result['소계'], c = data_result['오차'], s = 50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')

# 0~9까지 위에 있는 10개 지역을 text로 표시하는 for문
for n in range(10):
    # x 값으로부터 1.02, y 값으로부터 0.98만큼 떨어진 곳에 text를 표시하고
    # fontsize는 15
    plt.text(df_sort['인구수'][n]*1.02, df_sort['소계'][n]*0.98,
            df_sort.index[n], fontsize=15)

plt.xlabel('인구수')
plt.ylabel('인구당비율')

plt.grid()
plt.show()

 

 

※ 자주 사용하는 주피터노트북 옵션

- 처음부터 전체 실행할 때 사용한다.

 

- 복사본을 만들어놓고 작업할 때 사용한다.

 

- 출력결과만 따로 확인할 때 사용한다.

 

- 작성한 코드를 파이썬 소스로 다운로드할 때 사용한다.

 

- 기존에 실행했었던 남아있는 내용을 지우고 재실행할 때 사용한다.

 

- 소스 라인을 표시할 때 사용한다.

 

 

'Programming Language > Python' 카테고리의 다른 글

[File_Filter] 파일 분류하는 방법  (0) 2019.08.06
함수 기초  (0) 2018.10.03
자료타입  (0) 2018.09.15
파이썬 기본문법  (0) 2018.09.04
Anaconda3 설치방법  (0) 2018.09.04

댓글