[practice Project] Analysis Seoul CCTV(2)

전주미·2024년 4월 8일

practice Project

목록 보기
2/2
post-thumbnail

앞서 서울 '구'별 인구대비 CCTV 비율에 대한 데이터 정리는 끝이 났다.
이번에는 그 데이터들을 바탕으로 데이터 시각화를 해보려고 한다.

import matplotlib.pyplot as plt
from matplotlib import rc

plt.rcParams["axes.unicode_minus"] = False 
#마이너스 부호 때문에 한글이 깨질 수 가 있어 주는 설정

rc("font", family="Arial Unicode Ms")

%matplotlib inline
data_result.head()

1. "소계" 컬럼 시각화

data_result["소계"].plot(kind="barh", grid=True, figsize=(10, 10));

그런데 눈에 데이터가 잘 들어오지 않아 정렬을 해보려고 한다.
그 결과 강남구부터 내림차순으로 그래프로 나타나게 된다.

data_result["소계"].sort_values().plot(kind="barh", grid=True, figsize=(10, 10));

그리고 이것을 함수에 담아 주겠다.

def drawGraph
	data_result["소계"].sort_values().plot(
    	kind="barh", grid=True, figsize=(10, 10));
drawGraph()

여기에서 그래프의 정보를 더하여 주기 위해 title 등을 추가해 주겠다.

def drawGraph():
    data_result["소계"].sort_values().plot(
    kind="barh",
    grid=True,
    title="가장 CCTV가 많은 구",
    figsize=(10, 10));
drawGraph()

결과적으로, 절대적인 CCTV 대수를 내림차순으로 정렬한 결과, 강남구가 가장 많은 CCTV를 설치한 것으로 나타났다.

2. "CCTV 비율" 컬럼 시각화

이번에는 CCTV비율로 시각화를 해보자.

data_result.head()

def drawGraph():
    data_result["CCTV비율"].sort_values().plot(
    kind="barh",
    grid=True,
    title="가장 CCTV가 많은 구",
    figsize=(10, 10));
drawGraph()

CCTV 비율로 내림차순 정렬을 하였더니, 종로구가 가장 인구대비 CCTV가 많은 것으로 나타났다.

비고)
kind : str

The kind of plot to produce:

‘line’ : line plot (default)

‘bar’ : vertical bar plot

‘barh’ : horizontal bar plot

‘hist’ : histogram

‘box’ : boxplot

‘kde’ : Kernel Density Estimation plot

‘density’ : same as ‘kde’

‘area’ : area plot

‘pie’ : pie plot

‘scatter’ : scatter plot (DataFrame only)

‘hexbin’ : hexbin plot (DataFrame only)

3. 데이터 경향 표시

data_result.head()

인구수와 소계 컬럼으로 scatter plot을 그려보려고 한다.

가로축이 14, 세로축이 10인 크기의 figsize를 설정해주고, x축 = 인구수, y축 = CCTV로 설정하여 그래프를 그려보면 다음과 같다.

def drawGraph():
    plt.figure(figsize=(14, 10))
    plt.scatter(data_result["인구수"], data_result["소계"], s=50)
    plt.xlabel("인구수")
    plt.ylabel("CCTV")
    plt.grid(True)
    plt.show()
drawGraph()

4. Numpy를 이용한 1차 직선 만들기

  • np.polyfit : 직선을 구성하기 위한 계수 계산(y절편과 기울기)
  • np.poly1d : polyfit으로 찾은 계수로 python에서 사용할 함수로 만들어 줌

polyfit method를 불러와 x축=인구수 y축=소계 로 설정해주면,이 두 변수에 대한 직선의 계수가 나오게 된다.

import numpy as np
fpl=np. polyfit(data_result["인구수"], data_result["소계"], 1)
fpl

계수 값이 1.11155868e-03, 1.06515745e+03

이것을 f1라는 변수로 poly1d를 써서 여기에 담아준다.

f1 = np.poly1d(fpl)
f1

f1(400000)

- 인구가 40만인 구에서 서울시의 전체 경향에 맞는 적당한 CCTV수는?

- 1500개 정도 있어야할 것이다 라는 결론!


그렇다면, scatter plot에 추가해 줄 그래프를 만들어 보겠다.

fx = np.linspace(100000, 700000, 100)
  • 경향선을 그리기 위한 X데이터 생성
  • np.linspace(a, b, n): a부터 b까지 n개의 등간격 데이터 생성
  • 10만부터 70만까지 사이에 100개 데이터를 등간격으로 생성하라는 의미

경향선을 추가한 그래프를 그려보자.

def drawGraph():
    plt.figure(figsize=(14, 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(True)
    plt.show()
drawGraph()

직선을 넣어서 경향을 보여준다.

만약 인구가 50만인 구가 있다면, 그 구는 CCTV를 1600대 정도 가져야한다(서울시 기준)

5. 강조하고 싶은 데이터 시각화하기

  • 그래프 다듬기(경향과의 오차 만들기)
    1) 경향(trend)과의 오차를 만들자
    2) 경향은 f1 함수에 해당 인구를 입력
    3) f1(data_result["인구수"])
fp1 = np.polyfit(data_result["인구수"], data_result["소계"], 1)
f1 = np.poly1d(fp1)
fx = np.linspace(100000, 700000, 100)

data_result.head(3)

오차를 계산하여 오차 컬럼을 추가해보려고 한다.
오차 = 소계 - f1값에 넣어준 인구수 로 정의해볼 수 있다.

data_result["오차"] = data_result["소계"] - f1(data_result["인구수"])
data_result.head()

경향과 비교해서 데이터의 오차가 너무 많이 나는 경우의 데이터를 계산해 주면,

# 경향과 비교해서 데이터의 오차가 너무 나는 데이터를 계산

df_sort_f = data_result.sort_values(by="오차", ascending=False) #내림차순
df_sort_t = data_result.sort_values(by="오차", ascending=True) #오름차순
# 경향 대비 CCTV를 많이 가진 구
df_sort_f.head()

# 경향 대비 CCTV를 적게 가진 구
df_sort_t.head()

이렇게 데이터 결과가 나왔다면,
위 그래프에, 경향에 따른 높고 낮음을 색깔로 표현해보고자 한다.

matplotlib의 colors라는 서브패키지를 가져와서 ListedColormap이라는 기능을 가져온다.
그리고 color_step이라는 리스트 안에 6개의 색깔을 지정한다.
my_cmap이라는 변수를 두고 ListedColormap객체를 가져와서 color_step변수를 넣어 준다.

from matplotlib.colors import ListedColormap
# colormap 을 사용자 정의(user define)로 세팅

color_step = ["#e74c3c", "#2ecc71", "#95a9a6", "#2ecc71", "#3498db", "#3498db"]
my_cmap = ListedColormap(color_step)

다음으로,
기존 그래프에 기능을 하나씩 추가해보자.

그러기 위해서 경향에서 가장 벗어나 있는 데이터 값이 무엇인지 확인을 해보겠다.

data_result.head()

오차비율이 가장 큰 date_result의 index[0]을 살펴보면,
강남구가 인구수 약 5만6천명에 CCTV가 약3200대로써 오차가 가장 크다고 판단되었다.

data_result["인구수"][0]

'561052'

data_result["소계"][0]

'3238'

data_result.index[0]

'강남구'

그렇다면, text method에 인구수, 소계, 인덱스, 폰트사이즈를 추가해준다.

def drawGraph():
    plt.figure(figsize=(14, 10))
    plt.scatter(data_result["인구수"], data_result["소계"], s=50, c=data_result["오차"], cmap=my_cmap)
    plt.plot(fx, f1(fx), ls="dashed", lw=3, color="g")
    plt.text(df_sort_f["인구수"][0] * 1.02, df_sort_f["소계"][0] * 0.98, df_sort_f.index[0], fontsize=15)
    plt.xlabel("인구수")
    plt.ylabel("CCTV")
    plt.colorbar()
    plt.grid(True)
    plt.show()
drawGraph()

다음과 같이 경향성이 가장 크게 벗어난 지점에 '강남구'가 표시된다.

이를 for문으로 반복하여 상위 5개, 하위 5개의 '구'를 그래프 위에 표시해보자.

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

    for n in range(5):
    # 상위 5개
        plt.text(
            df_sort_f["인구수"][n] * 1.02, # x좌표
            df_sort_f["소계"][n] * 0.98, # y좌표
            df_sort_f.index[n],
            fontsize=15
        )
    
    # 하위 5개
        plt.text(
            df_sort_t["인구수"][n] * 1.02,
            df_sort_t["소계"][n] * 0.98,
            df_sort_t.index[n],
            fontsize=15
        )
    
    
    plt.xlabel("인구수")
    plt.ylabel("CCTV")
    plt.colorbar()
    plt.grid(True)
    plt.show()
drawGraph()

위와 같이 초록색 직선의 경향선을 기준으로 상위 5개, 하위 5개의 '구'를 확인할 수 있다.
서울시 CCTV수와 인구수의 데이터를 병합하여 이와 같은 결론이 나온셈이다.

  • 저장
data_result.to_csv("../data/01. CCTV_result.csv", sep=",", encoding="utf-8")
profile
data dreamer

0개의 댓글