no image
[딥러닝] CNN _ 도로 교통 표지판 인식
1. 데이터 가져오기 - 43개의 교통 표지판 과 관련된 4만여 개의 이미지로 만들어진 데이터 셋 - https://sid.erda.dk/public/archives/daaeac0d7ce1152aea9b61d9f1e19370/published-archive.html Public Archive: daaeac0d7ce1152aea9b61d9f1e19370 Support ERDA User Guide Questions about ERDA? Please contact us at support@erda.dk sid.erda.dk - https://drive.google.com/drive/folders/1AktNCWqVBv-4xxme4OUC82OLJTHzMFsq?usp=sharing - 데이터 다운로드 colab !..
2024.03.22
no image
[딥러닝] CNN _ 패션 이미지 분류
1. 데이터 가져오기 from tensorflow import keras from sklearn.model_selection import train_test_split #데이터 읽어오기 (train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data() rint(train_input.shape) 2. 스케일링 및 차원 수정 # 흑백 이미지라서 차원을 1개 늘려서 255 로 나눔 train_scaled = train_input.reshape(-1, 28, 28, 1) / 255.0 print(train_scaled.shape) train_scaled, val_scaled, train_target, v..
2024.03.22
no image
[전처리] 인구 소멸 지역 Cartogram
- 국가 통계 포털에서 인구 정보 내려받기 http://kosis.kr/statHtml/statHtml.do?orgId=101&tblId=DT_1IN1509& KOSIS kosis.kr 기본코드 import pandas as pd import numpy as np import platform import matplotlib.pyplot as plt %matplotlib inline path = "c:/Windows/Fonts/malgun.ttf" from matplotlib import font_manager, rc if platform.system() == 'Darwin': rc('font', family='AppleGothic') elif platform.system() == 'Windows': fo..
2024.03.20
no image
[전처리] 서울시 범죄 현황 시각화
- https://data.seoul.go.kr/ - 서울시 5대 점죄 발생현황 통계 데이터 - crime.txt 1. 데이터 가져오기 import pandas as pd import folium import json criminal = pd.read_csv('./data/crime.txt', sep='\t', thousands=',', encoding='utf-8') 2. 데이터 전처리 # 불필요한 컬럼 제거 criminal.drop(['기간', '합계검거', '살인검거', '강도검거', '강간검거', '절도검거', '폭력검거'], axis=1, inplace=True) criminal.drop([0], inplace=True) # 컬럼 이름 설정 criminal.rename(columns={'살인발생..
2024.03.20
no image
[전처리] 지도 출력 Choropleth
https://github.com/southkorea/southkorea-maps 한국 지리정보 사이트 1. 데이터 읽어오기 state_unemployment = 'data/US_Unemployment_Oct2012.csv' state_data = pd.read_csv(state_unemployment) print(state_data.head()) State Unemployment 0 AL 7.1 1 AK 6.8 2 AZ 8.1 3 AR 7.2 4 CA 10.1 2. 단계 구분도 출력 folium.Map으로 만든 지도에 choropleth 함수 호출 geo_data 옵션에 아이디와 위도 및 경도를 가진 json 파일의 경로를 설정해주고 data에 출력한 DF 설정 key_on에 json 파일에서 사용되는..
2024.03.20
no image
[전처리] 서울시 구별 CCTV와 인구 관계 분석
1. 서울시 자치구별 CCTV 현황 데이터 가져오기 - http://data.seoul.go.kr/ 열린데이터광장 메인 데이터분류,데이터검색,데이터활용 data.seoul.go.kr - CCTV 검색 - xlsx 파일을 다운로드(파일 이름을 cctv로 수정) # 데이터 읽기 import matplotlib.pyplot as plt import pandas as pd import numpy as np from pandas import Series, DataFrame import platform from matplotlib import font_manager, rc #데이터 읽어오기 cctv = pd.read_excel('./data/cctv.xlsx') print(cctv.head()) cctv.info(..
2024.03.20
no image
[딥러닝] Keras _ 패션 이미지 분류
Keras 의 내장 데이터 세트 - 종류 boston housing: 보스톤 집값으로 회귀에 사용 cifar10: 이미지 분류 데이터로 종류가 10가지 cifar100: 이미지 분류 데이터로 종류가 100가지 mnist: 손 글씨 데이터 fashion mnist: 의류 데이터로 클래스가 10가지 imdb: 영화 데이터로 분류에 활용 reuters: 뉴스 토픽 - 자연어 분류 - load_data 라는 함수를 호출하면 훈련 데이터 튜플 과 테스트 데이터 튜플로 데이터를 리턴 - 각각의 튜플은 피처 와 레이블로 나뉘어져 있음 1. 데이터 가져오기 fashion_mnist = keras.datasets.fashion_mnist (X_train_full, y_train_full), (X_test, y_test)..
2024.03.20
no image
[딥러닝] Keras 이항분류 _ 레드와 화이트와인 분류
데이터 url red: http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv white: http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality- white.csv - 구분자: ; - 피처 fixed acidity volatile acidity citric acid residual sugar chlorides free sulfur dioxide total sulfur dioxide density pH sulphates alcohol quality 1. 데이터 가져오기 import pandas as pd red..
2024.03.20

1. 데이터 가져오기

- 43개의 교통 표지판 과 관련된 4만여 개의 이미지로 만들어진 데이터 셋

- https://sid.erda.dk/public/archives/daaeac0d7ce1152aea9b61d9f1e19370/published-archive.html

 

Public Archive: daaeac0d7ce1152aea9b61d9f1e19370

Support ERDA User Guide Questions about ERDA? Please contact us at support@erda.dk

sid.erda.dk

- https://drive.google.com/drive/folders/1AktNCWqVBv-4xxme4OUC82OLJTHzMFsq?usp=sharing

 

 

- 데이터 다운로드

   colab
	!wget https://sid.erda.dk/public/archives/daaeac0d7ce1152aea9b61d9f1e19370/GTSRB_Final_Training_Images.zip
	!unzip -qq "GTSRB_Final_Training_Images.zip"
  • GTSRB\Final_Training\Images 디렉토리 안에 디렉토리 별로 이미지가 ppm 확장자로 분류되어 있다.

 

- ppm: 무손실 압축 이미지 파일 형식

 

- image 크기: 32 x 32

 

 

- 신경망 구조를 만들 때 선택 사항

  • 2차원 컨볼루션에서 필터 개수 와 커널 크기
  • Max Pool 에서의 커널 크기
  • Fully Connected 계층(Dense)에서의 유닛 개수
  • 계층 별 배치 크기, 최적화 알고리즘, 학습률, 할성화 함수, epoch 수 등

 

- 이미지 전처리

  • 이미지 크기를 32*32로 수정
  • 43개의 레이블을 생성: 디렉토리 이름이 레이블
  • 표지판의 경우 구분해야 할 것은 색상이 아니라 모양: 이미지를 흑백으로 변경
  • 데이터를 다룰 때 목적을 먼저 생각하고 작업을 시작해야 한다. (도메인)

 

- 필요한 패키지 와 상수 선언

  • 클래스 개수와 이미지의 크기를 변수에 저장
  • 이 두개의 절대로 변하지 않을 값
  • 변하지 않는 값을 장할 변수의 이름은 모두 대문자로 작성하는 것이 좋다.
    • SNAKE 표기법이라고 한다.
N_CLASSES = 43
RESIZED_IMAGE = (32, 32)

 

 

# 모듈

import matplotlib.pyplot as plt
#디렉토리를 핸들링 할 때 사용하는 모듈
import glob

from skimage.color import rgb2lab
from skimage.transform import resize
import numpy as np

from collections import namedtuple

from sklearn.model_selection import train_test_split

 

 

- 이미지 전처리를 위한 함수

  • 이름이 있는 튜플 생성 - 다른 언어에서는 튜플을 이런 형태로 만든다.
  • 튜플의 원래 목적은 변경할 수 없는 하나의 행(record, row)을 표현하기 위한 것
  • 인덱스가 아니라 이름으로 구별하는 것이 타당
Dataset = namedtuple('Dataset', ['X', 'y'])

 

 

# 포맷 변경해주는 함수

def to_tf_format(imgs):
    return np.stack([img[:, :, np.newaxis] for img in imgs], axis=0).astype(np.float32)

 

 

- 이미지 디렉토리에 있는 모든 이미지들에 라벨링을 하기 위한 작업
- 이미지의 크기 변경도 여기서 수행

def read_dataset_ppm(rootpath, n_labels, resize_to):
    #이미지 데이터 와 라벨을 저장할 list
    images = []
    labels = []
    
    for c in range(n_labels):
        #루트경로/00001/ 형태로 이미지 경로를 생성
        full_path = rootpath + "/" + format(c, '05d') + "/"
        #각 이미지 디렉토리를 순회하면서 확장자가 ppm 인 파일의 경로를 가지고
        for img_name in glob.glob(full_path + "*.ppm"):
            #이미지 읽어오기
            img = plt.imread(img_name).astype(np.float32)
            #이미지를 정규화
            img = rgb2lab(img / 255.0)[:, :,0]
            #이미지 크기를 조정
            if resize_to:
                img = resize(img, resize_to, mode='reflect')
            #라벨 생성
            #43개 짜리 배열을 만들어서 자신의 인덱스에 해당하는 값에만 1을 대입
            #원핫 인코딩 된 라벨
            label = np.zeros((n_labels, ), dtype=np.float32) 
            label[c] = 1.0
            
            images.append(img.astype(np.float32))
            labels.append(label)

    return Dataset(X = to_tf_format(images).astype(np.float32), y = np.matrix(labels).astype(np.float32))

 

 

# 이미지 전처리를 수행해서 데이터 만들기

dataset = read_dataset_ppm('./data/GTSRB/Final_Training/Images',
                          N_CLASSES,
                          RESIZED_IMAGE)
print(dataset.X.shape)
print(dataset.y.shape)
(39209, 32, 32, 1)
(39209, 43)

 

 

# 이미지 확인

plt.imshow(dataset.X[0, :, :, :].reshape(RESIZED_IMAGE))
print(dataset.y[0, :])
[[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

 

 

 

- 훈련 데이터와 테스트 데이터 분리

#훈련 데이터 와 테스트 데이터 분리
idx_train, idx_test = train_test_split(range(dataset.X.shape[0]), test_size=0.25,
                                      random_state=42)

X_train = dataset.X[idx_train, :, :, :]
X_test = dataset.X[idx_test, :, :, :]

y_train = dataset.y[idx_train, :]
y_test = dataset.y[idx_test, :]

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(29406, 32, 32, 1)
(9803, 32, 32, 1)
(29406, 43)
(9803, 43)

 

 

- 모델 생성

model = keras.Sequential()

#입력층 - Convolution 
model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu',
                             padding='same', input_shape=(32, 32, 1)))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Conv2D(64, kernel_size=3, activation='relu',
                             padding='same'))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Conv2D(128, kernel_size=3, activation='relu',
                             padding='same'))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Flatten())

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(43, activation='softmax'))

model.summary()

 

 

- 모델 컴파일

model.compile(optimizer='adam',
             loss='categorical_crossentropy',
             metrics=['accuracy'])

 

 

- 훈련

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-cnn-models.keras')
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2,
                                                 restore_best_weights=True)
history = model.fit(X_train, y_train, epochs=20,
                   callbacks = [checkpoint_cb, early_stopping_cb])

 

 

- 평가

print(model.evaluate(X_test, y_test))
307/307 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.8567 - loss: 0.3981
[0.39652204513549805, 0.853820264339447]

 

!git clone https://github.com/MachineLearning2020/Homework2-Caltech101.git

 

 

 

 

 

 

 

1. 데이터 가져오기

from tensorflow import keras
from sklearn.model_selection import train_test_split

#데이터 읽어오기
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
rint(train_input.shape)

 

 

 

 

2. 스케일링 및 차원 수정

# 흑백 이미지라서 차원을 1개 늘려서 255 로 나눔
train_scaled = train_input.reshape(-1, 28, 28, 1) / 255.0
print(train_scaled.shape) 

train_scaled, val_scaled, train_target, val_target = train_test_split(train_scaled, train_target, test_size=0.2, random_state=42)
(60000, 28, 28, 1)

 

 

 

 

3. 모델 생성

#모델 만들기
model = keras.Sequential()

#입력 층을 합성곱 층을 사용
model.add(keras.layers.Conv2D(32, kernel_size=3, activation="relu",
                             padding='same', input_shape=(28, 28, 1)))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Conv2D(64, kernel_size=3, activation="relu",
                             padding='same'))
model.add(keras.layers.MaxPooling2D(2))

#데이터를 1차원으로 만들어주는 층
model.add(keras.layers.Flatten())

#Dense 는 1차원의 데이터만 사용
model.add(keras.layers.Dense(100, activation="relu"))

#드랍 아웃 적용
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(10, activation="softmax"))

 

# 모델 확인

model.summary()

 

 

 

 

 

4. 모델 컴파일 (훈련)

model.compile(optimizer="adam", loss="sparse_categorical_crossentropy",
             metrics=["accuracy"])

#체크 포인트
checkpoint_cb = keras.callbacks.ModelCheckpoint("best-cnn-model.keras")

#2번의 epoch 동안 점수가 좋아지지 않으면 조기 종료
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2,
                                                 restore_best_weights=True)
history = model.fit(train_scaled, train_target, epochs=20,
                   validation_data=(val_scaled, val_target),
                   callbacks=[checkpoint_cb, early_stopping_cb])

 

 

 

 

 

4. 모델 평가

model.evaluate(val_scaled, val_target)
  • 아래 합성곱 층으로 내려갈 때 뉴런의 개수를 늘렸는데 중간에 MaxPooling2D(Dropout을 적용해도 동일) 을 적용했기 때문에 실제 파라미터의 개수가 줄어들어 사용되므로 늘린 티가 나지 않음
#클래스 이름
classes = ['티셔츠', '바지', '스웨터', '드레스', '코트',
          '샌달', '셔츠', '스니커즈', '가방', '앵클 부츠']


preds = model.predict(val_scaled[0:1])
print(preds)
print(classes[np.argmax(preds)])

plt.imshow(val_scaled[0].reshape(28, 28), cmap='gray_r')
plt.show()

 

 

# 훈련에 사용한 데이터가 스케일되어 있으므로, 예측 데이터도 스케일링

test_scaled = test_input.reshape(-1, 28, 28, 1)/255.0
model.evaluate(test_scaled, test_target)

 

 

# 모델 저장

model.save("best-cnn-models.keras")
  • 이전 API에서는 확장자를 h5를 권장했는데 최근에는 keras를 권장

 

 

 

- 국가 통계 포털에서 인구 정보 내려받기

http://kosis.kr/statHtml/statHtml.do?orgId=101&tblId=DT_1IN1509&

 

KOSIS

 

kosis.kr

 

기본코드

import pandas as pd
import numpy as np
import platform
import matplotlib.pyplot as plt

%matplotlib inline
path = "c:/Windows/Fonts/malgun.ttf"
from matplotlib import font_manager, rc
if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('알 수 없는 시스템')    
plt.rcParams['axes.unicode_minus'] = False

 

 

 

 

1. 데이터 읽어오기

population = pd.read_excel('./data/population.xlsx', header=1)
population

 

 

 

 

2. 데이터 전처리

 

#결측치를 앞의 데이터로 채우기

population.fillna(method='ffill', inplace=True)

 

 

#컬럼이름 변경하기

population.rename(columns = {'행정구역별(시군구)(1)':'광역시도', 
                             '행정구역별(시군구)(2)':'시도', 
                             '합계':'인구수'}, inplace=True)
population.head()

 

 

# 소계를 제외한 데이터만 가져오기

population = population[(population['시도'] != '소계')]
population.head()

 

 

# 컬럼 이름 변경

population.is_copy = False
population.rename(columns = {'성별(1)':'구분'}, inplace=True)
population.loc[population['구분'] == '계', '구분'] = '합계'
population.head()

 

 

# 청년과 노년 구분

population['20-39세'] = population['20~24세'] + population['25~29세'] + \
                        population['30~34세'] + population['35~39세']
    
population['65세이상'] = population['65~69세'] + population['70~74세'] + \
                        population['75~79세'] + population['80~84세'] + \
                        population['85세이상']

population.head(5)

 

 

# 피벗 테이블 생성

pop = pd.pivot_table(population, 
                     index = ['광역시도', '시도'], 
                     columns = ['구분'],
                     values = ['인구수', '20-39세', '65세이상'])
pop.head()

 

 

# 새로운 컬럼 추가 - 소멸 비율

pop['소멸비율'] = pop['20-39세','합계'] / (pop['65세이상','합계'] / 2)
pop.head()

 

 

# 새로운 컬럼 추가 - 소멸 위기 지역

pop['소멸위기지역'] = pop['소멸비율'] < 1.0
pop.head()
print(pop[pop['소멸위기지역']==True].index.get_level_values(1))
Index(['영월군', '남해군', '산청군', '의령군', '하동군', '함양군', '합천군', '군위군', '면부', '봉화군',
       '영덕군', '영양군', '의성군', '청도군', '청송군', '강화군', '면부', '강진군', '고흥군', '곡성군',
       '구례군', '면부', '보성군', '신안군', '완도군', '장흥군', '진도군', '함평군', '해남군', '고창군',
       '면부', '무주군', '부안군', '순창군', '임실군', '장수군', '진안군', '부여군', '서천군', '청양군',
       '괴산군', '단양군', '보은군'],
      dtype='object', name='시도')

 

 

# 인덱스 초기화

pop.reset_index(inplace=True) 
pop.head()

 

 

 

 

 

3. 시도 이름 합치기

# 새로운 컬럼 추가 - 첫번째와 두번째 컬럼 이름 합치기

tmp_coloumns = [pop.columns.get_level_values(0)[n] + \
                pop.columns.get_level_values(1)[n] 
                for n in range(0,len(pop.columns.get_level_values(0)))]

pop.columns = tmp_coloumns
pop.head()

 

 

# 데이터 확인

pop.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 303 entries, 0 to 302
Data columns (total 13 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   광역시도      303 non-null    object 
 1   시도        303 non-null    object 
 2   20-39세남자  303 non-null    int64  
 3   20-39세여자  303 non-null    int64  
 4   20-39세합계  303 non-null    int64  
 5   65세이상남자   303 non-null    int64  
 6   65세이상여자   303 non-null    int64  
 7   65세이상합계   303 non-null    int64  
 8   인구수남자     303 non-null    int64  
 9   인구수여자     303 non-null    int64  
 10  인구수합계     303 non-null    int64  
 11  소멸비율      303 non-null    float64
 12  소멸위기지역    303 non-null    bool   
dtypes: bool(1), float64(1), int64(9), object(2)
memory usage: 28.8+ KB

 

 

# 시도 고유값 확인

print(pop['시도'].unique())
['강릉시' '고성군' '동부' '동해시' '면부' '삼척시' '속초시' '양구군' '양양군' '영월군' '원주시' '읍부'
 '인제군' '정선군' '철원군' '춘천시' '태백시' '평창군' '홍천군' '화천군' '횡성군' '가평군' '고양시' '과천시'
 '광명시' '광주시' '구리시' '군포시' '권선구' '기흥구' '김포시' '남양주시' '단원구' '덕양구' '동두천시' '동안구'
 '만안구' '부천시' '분당구' '상록구' '성남시' '수원시' '수정구' '수지구' '시흥시' '안산시' '안성시' '안양시'
 '양주시' '양평군' '여주시' '연천군' '영통구' '오산시' '용인시' '의왕시' '의정부시' '이천시' '일산동구'
 '일산서구' '장안구' '중원구' '처인구' '파주시' '팔달구' '평택시' '포천시' '하남시' '화성시' '거제시' '거창군'
 '김해시' '남해군' '마산합포구' '마산회원구' '밀양시' '사천시' '산청군' '성산구' '양산시' '의령군' '의창구'
 '진주시' '진해구' '창녕군' '창원시' '통영시' '하동군' '함안군' '함양군' '합천군' '경산시' '경주시' '고령군'
 '구미시' '군위군' '김천시' '남구' '문경시' '봉화군' '북구' '상주시' '성주군' '안동시' '영덕군' '영양군'
 '영주시' '영천시' '예천군' '울릉군' '울진군' '의성군' '청도군' '청송군' '칠곡군' '포항시' '광산구' '동구'
 '서구' '달서구' '달성군' '수성구' '중구' '대덕구' '유성구' '강서구' '금정구' '기장군' '동래구' '부산진구'
 '사상구' '사하구' '수영구' '연제구' '영도구' '해운대구' '강남구' '강동구' '강북구' '관악구' '광진구' '구로구'
 '금천구' '노원구' '도봉구' '동대문구' '동작구' '마포구' '서대문구' '서초구' '성동구' '성북구' '송파구' '양천구'
 '영등포구' '용산구' '은평구' '종로구' '중랑구' '세종시' '울주군' '강화군' '계양구' '남동구' '미추홀구' '부평구'
 '연수구' '옹진군' '강진군' '고흥군' '곡성군' '광양시' '구례군' '나주시' '담양군' '목포시' '무안군' '보성군'
 '순천시' '신안군' '여수시' '영광군' '영암군' '완도군' '장성군' '장흥군' '진도군' '함평군' '해남군' '화순군'
 '고창군' '군산시' '김제시' '남원시' '덕진구' '무주군' '부안군' '순창군' '완산구' '완주군' '익산시' '임실군'
 '장수군' '전주시' '정읍시' '진안군' '서귀포시' '제주시' '계룡시' '공주시' '금산군' '논산시' '당진시' '동남구'
 '보령시' '부여군' '서북구' '서산시' '서천군' '아산시' '예산군' '천안시' '청양군' '태안군' '홍성군' '괴산군'
 '단양군' '보은군' '상당구' '서원구' '영동군' '옥천군' '음성군' '제천시' '증평군' '진천군' '청원구' '청주시'
 '충주시' '흥덕구']

 

 

# 시도 고유 이름

si_name = [None] * len(pop)

#광역시가 아닌 곳 중에서 구를 가지고 있는 시도들의 구이름 디셔너리 생성
tmp_gu_dict = {'수원':['장안구', '권선구', '팔달구', '영통구'], 
                       '성남':['수정구', '중원구', '분당구'], 
                       '안양':['만안구', '동안구'], 
                       '안산':['상록구', '단원구'], 
                       '고양':['덕양구', '일산동구', '일산서구'], 
                       '용인':['처인구', '기흥구', '수지구'], 
                       '청주':['상당구', '서원구', '흥덕구', '청원구'], 
                       '천안':['동남구', '서북구'], 
                       '전주':['완산구', '덕진구'], 
                       '포항':['남구', '북구'], 
                       '창원':['의창구', '성산구', '진해구', '마산합포구', '마산회원구'], 
                       '부천':['오정구', '원미구', '소사구']}

for n in pop.index:
    #고성이 2곳이므로 도를 추가
    if pop['광역시도'][n][-3:] not in ['광역시', '특별시', '자치시']:
        if pop['시도'][n][:-1]=='고성' and pop['광역시도'][n]=='강원도':
            si_name[n] = '고성(강원)'
        elif pop['시도'][n][:-1]=='고성' and pop['광역시도'][n]=='경상남도':
            si_name[n] = '고성(경남)'
        #그 이외의 지역은 마지막 한글자를 제거해서 군 이나 시 글자를 제거
        else:
             si_name[n] = pop['시도'][n][:-1]
        for keys, values in tmp_gu_dict.items():
            if pop['시도'][n] in values:
                if len(pop['시도'][n])==2:
                    si_name[n] = keys + ' ' + pop['시도'][n]
                elif pop['시도'][n] in ['마산합포구','마산회원구']:
                    si_name[n] = keys + ' ' + pop['시도'][n][2:-1]
                else:
                    si_name[n] = keys + ' ' + pop['시도'][n][:-1]
                        #세종은 이름을 수정    
    elif pop['광역시도'][n] == '세종특별자치시':
        si_name[n] = '세종'
    else:
        if len(pop['시도'][n])==2:
            si_name[n] = pop['광역시도'][n][:2] + ' ' + pop['시도'][n]
        else:
            si_name[n] = pop['광역시도'][n][:2] + ' ' + pop['시도'][n][:-1]
print(si_name)
['강릉', '고성(강원)', '동', '동해', '면', '삼척', '속초', '양구', '양양', '영월', '원주', '읍', '인제', '정선', '철원', '춘천', '태백', '평창', '홍천', '화천', '횡성', '가평', '고양', '과천', '광명', '광주', '구리', '군포', '수원 권선', '용인 기흥', '김포', '남양주', '안산 단원', '고양 덕양', '동두천', '동', '안양 동안', '안양 만안', '면', '부천', '성남 분당', '안산 상록', '성남', '수원', '성남 수정', '용인 수지', '시흥', '안산', '안성', '안양', '양주', '양평', '여주', '연천', '수원 영통', '오산', '용인', '읍', '의왕', '의정부', '이천', '고양 일산동', '고양 일산서', '수원 장안', '성남 중원', '용인 처인', '파주', '수원 팔달', '평택', '포천', '하남', '화성', '거제', '거창', '고성(경남)', '김해', '남해', '동', '창원 합포', '창원 회원', '면', '밀양', '사천', '산청', '창원 성산', '양산', '읍', '의령', '창원 의창', '진주', '창원 진해', '창녕', '창원', '통영', '하동', '함안', '함양', '합천', '경산', '경주', '고령', '구미', '군위', '김천', '포항 남구', '동', '면', '문경', '봉화', '포항 북구', '상주', '성주', '안동', '영덕', '영양', '영주', '영천', '예천', '울릉', '울진', '읍', '의성', '청도', '청송', '칠곡', '포항', '광주 광산', '광주 남구', '광주 동구', '광주 북구', '광주 서구', '대구 남구', '대구 달서', '대구 달성', '대구 동구', '대구 동부', '대구 면부', '대구 북구', '대구 서구', '대구 수성', '대구 읍부', '대구 중구', '대전 대덕', '대전 동구', '대전 서구', '대전 유성', '대전 중구', '부산 강서', '부산 금정', '부산 기장', '부산 남구', '부산 동구', '부산 동래', '부산 동부', '부산 면부', '부산 부산진', '부산 북구', '부산 사상', '부산 사하', '부산 서구', '부산 수영', '부산 연제', '부산 영도', '부산 읍부', '부산 중구', '부산 해운대', '서울 강남', '서울 강동', '서울 강북', '서울 강서', '서울 관악', '서울 광진', '서울 구로', '서울 금천', '서울 노원', '서울 도봉', '서울 동대문', '서울 동작', '서울 마포', '서울 서대문', '서울 서초', '서울 성동', '서울 성북', '서울 송파', '서울 양천', '서울 영등포', '서울 용산', '서울 은평', '서울 종로', '서울 중구', '서울 중랑', '세종', '세종', '세종', '세종', '울산 남구', '울산 동구', '울산 동부', '울산 면부', '울산 북구', '울산 울주', '울산 읍부', '울산 중구', '인천 강화', '인천 계양', '인천 남동', '인천 동구', '인천 동부', '인천 면부', '인천 미추홀', '인천 부평', '인천 서구', '인천 연수', '인천 옹진', '인천 읍부', '인천 중구', '강진', '고흥', '곡성', '광양', '구례', '나주', '담양', '동', '면', '목포', '무안', '보성', '순천', '신안', '여수', '영광', '영암', '완도', '읍', '장성', '장흥', '진도', '함평', '해남', '화순', '고창', '군산', '김제', '남원', '전주 덕진', '동', '면', '무주', '부안', '순창', '전주 완산', '완주', '읍', '익산', '임실', '장수', '전주', '정읍', '진안', '동', '면', '서귀포', '읍', '제주', '계룡', '공주', '금산', '논산', '당진', '천안 동남', '동', '면', '보령', '부여', '천안 서북', '서산', '서천', '아산', '예산', '읍', '천안', '청양', '태안', '홍성', '괴산', '단양', '동', '면', '보은', '청주 상당', '청주 서원', '영동', '옥천', '음성', '읍', '제천', '증평', '진천', '청주 청원', '청주', '충주', '청주 흥덕']

 

 

#도시 이름을 DataFrame에 추가하고 불필요한 데이터 삭제

pop['ID'] = si_name

del pop['20-39세남자']
del pop['65세이상남자']
del pop['65세이상여자']

pop.head()

 

 

 

 

3. Cartogram을 위한 엑셀 파일 

 

 

# 엑셀 파일 읽기

draw_korea_raw = pd.read_excel('./data/draw_korea_raw.xlsx')
draw_korea_raw

 

 

 

 

 

 

4. stack으로 묶기

# 컬럼 이름을 인덱스로 만들기

draw_korea_raw_stacked = pd.DataFrame(draw_korea_raw.stack())
draw_korea_raw_stacked

 

 

# 인덱스 초기화

draw_korea_raw_stacked.reset_index(inplace=True)
draw_korea_raw_stacked

 

 

# 컬럼 이름 변경

draw_korea_raw_stacked.rename(columns={'level_0':'y', 'level_1':'x', 0:'ID'}, 
                              inplace=True)
draw_korea_raw_stacked

 

 

 

 

 

5. cartogram의 기본이 되는 지도 그리기

BORDER_LINES = [
    [(5, 1), (5,2), (7,2), (7,3), (11,3), (11,0)], # 인천
    [(5,4), (5,5), (2,5), (2,7), (4,7), (4,9), (7,9), 
     (7,7), (9,7), (9,5), (10,5), (10,4), (5,4)], # 서울
    [(1,7), (1,8), (3,8), (3,10), (10,10), (10,7), 
     (12,7), (12,6), (11,6), (11,5), (12, 5), (12,4), 
     (11,4), (11,3)], # 경기도
    [(8,10), (8,11), (6,11), (6,12)], # 강원도
    [(12,5), (13,5), (13,4), (14,4), (14,5), (15,5), 
     (15,4), (16,4), (16,2)], # 충청북도
    [(16,4), (17,4), (17,5), (16,5), (16,6), (19,6), 
     (19,5), (20,5), (20,4), (21,4), (21,3), (19,3), (19,1)], # 전라북도
    [(13,5), (13,6), (16,6)], # 대전시
    [(13,5), (14,5)], #세종시
    [(21,2), (21,3), (22,3), (22,4), (24,4), (24,2), (21,2)], #광주
    [(20,5), (21,5), (21,6), (23,6)], #전라남도
    [(10,8), (12,8), (12,9), (14,9), (14,8), (16,8), (16,6)], #충청북도
    [(14,9), (14,11), (14,12), (13,12), (13,13)], #경상북도
    [(15,8), (17,8), (17,10), (16,10), (16,11), (14,11)], #대구
    [(17,9), (18,9), (18,8), (19,8), (19,9), (20,9), (20,10), (21,10)], #부산
    [(16,11), (16,13)], #울산
#     [(9,14), (9,15)], 
    [(27,5), (27,6), (25,6)],
]

 

 

# 지역 이름 표시

plt.figure(figsize=(8, 11))

# 지역 이름 표시
for idx, row in draw_korea_raw_stacked.iterrows():
    
    # 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시
    # (중구, 서구)
    if len(row['ID'].split())==2:
        dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
    elif row['ID'][:2]=='고성':
        dispname = '고성'
    else:
        dispname = row['ID']

 

 

# 시도 경계 그리기

for path in BORDER_LINES:
    ys, xs = zip(*path)
    plt.plot(xs, ys, c='black', lw=1.5)

#y축의 위아래 변경
plt.gca().invert_yaxis()
#plt.gca().set_aspect(1)

#축과 라벨 제거
plt.axis('off')

#자동 레이아웃 설정
plt.tight_layout()
plt.show()

 

 

 

# draw_korea_raw_stacked와 pop의 도시이름 일치시키기

#draw_korea_raw_stacked 와 pop 의 도시이름 비교
print(set(draw_korea_raw_stacked['ID'].unique()) - set(pop['ID'].unique()))
print(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))

#일치하지 않는 데이터 삭제
tmp_list = list(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))

for tmp in tmp_list:
    pop = pop.drop(pop[pop['ID']==tmp].index)
                       
print(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))
{'부천 원미', '부천 오정', '부천 소사', '인천 남구'}
{'포항', '부산 면부', '대구 읍부', '성남', '용인', '인천 면부', '대구 동부', '인천 미추홀', '인천 동부', '안산', '부산 동부', '수원', '창원', '울산 면부', '읍', '인천 읍부', '부산 읍부', '고양', '천안', '동', '청주', '울산 읍부', '대구 면부', '안양', '전주', '면', '울산 동부', '부천'}
set()
pop = pd.merge(pop, draw_korea_raw_stacked, how='left', on=['ID'])
pop.head()

 

 

# 좌표와 인구수 이용해서 피벗 테이블 생성

mapdata = pop.pivot_table(index='y', columns='x', values='인구수합계')
mapdata

 

 

# Nan이 아닌 데이터 확인




 

 

 

 

 

 

6. Cartogram 그리기

# DataFrame과 컬럼 이름 및 색상 값을 받아서 Cartogram을 그려주는 함수

def drawKorea(targetData, blockedMap, cmapname):
    gamma = 0.75

    #인구수 데이터의 크고 낮음을 분류하기 위한 값 만들기
    whitelabelmin = (max(blockedMap[targetData]) - 
                                     min(blockedMap[targetData]))*0.25 + \
                                                                min(blockedMap[targetData])
    #컬럼이름을 대입하기
    datalabel = targetData
    #최대값과 최소값 구하기
    vmin = min(blockedMap[targetData])
    vmax = max(blockedMap[targetData])
    
    #x 와 y를 가지고 피봇 테이블 만들기
    mapdata = blockedMap.pivot_table(index='y', columns='x', values=targetData)
    #데이터가 존재하는 것 골라내기
    masked_mapdata = np.ma.masked_where(np.isnan(mapdata), mapdata)
    #그래프 영역 크기 만들기
    plt.figure(figsize=(9, 11))
    #색상 설정
    plt.pcolor(masked_mapdata, vmin=vmin, vmax=vmax, cmap=cmapname, 
               edgecolor='#aaaaaa', linewidth=0.5)
    
    # 지역 이름 표시
    for idx, row in blockedMap.iterrows():
        # 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시 
        #(중구, 서구)
        if len(row['ID'].split())==2:
            dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
        elif row['ID'][:2]=='고성':
            dispname = '고성'
        else:
            dispname = row['ID']
        # 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시
        if len(dispname.splitlines()[-1]) >= 3:
            fontsize, linespacing = 10.0, 1.1
        else:
            fontsize, linespacing = 11, 1.
            
        #글자색상 만들기
        annocolor = 'white' if row[targetData] > whitelabelmin else 'black'
        #텍스트 출력하기
        plt.annotate(dispname, (row['x']+0.5, row['y']+0.5), weight='bold',
                     fontsize=fontsize, ha='center', va='center', color=annocolor,
                     linespacing=linespacing)
    
    # 시도 경계 그리기
    for path in BORDER_LINES:
        ys, xs = zip(*path)
        plt.plot(xs, ys, c='black', lw=2)

    plt.gca().invert_yaxis()

    plt.axis('off')

    cb = plt.colorbar(shrink=.1, aspect=10)
    cb.set_label(datalabel)

    plt.tight_layout()
    plt.show()

 

 

 

# 인구수 합계로 Catrogram 그리기

drawKorea('인구수합계', pop, 'Blues')

 

 

#소멸위기지역으로 Cartogram 그리기

pop['소멸위기지역'] = [1 if con else 0 for con in pop['소멸위기지역']]
drawKorea('소멸위기지역', pop, 'Reds')

 

 

# 여성 비율로 Catrogram 그리기

pop['여성비'] = (pop['인구수여자']/pop['인구수합계'])*100
drawKorea('여성비', pop, 'RdBu')

 

 

# 청년 층의 여성 비율을 이용한 Cartogram 그리기

pop['2030여성비'] = (pop['20-39세여자']/pop['20-39세합계'])*100
drawKorea('2030여성비', pop, 'RdBu')

 

 

# 지역 이름을 index로 설정

import folium
import json
import warnings

warnings.simplefilter(action='ignore', category=FutureWarning)
pop_folium = pop.set_index('ID')
pop_folium.head()

 

 

#인구 수 합계를 이용한 단계구분도

#한국 지도 시도 경계 좌표를 가진 파일을 가져오기
geo_path = './data/korea_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))

map = folium.Map(location=[36.2002, 127.054], zoom_start=7)
folium.Choropleth(geo_data = geo_str,
               data = pop_folium['인구수합계'],
               columns = [pop_folium.index, pop_folium['인구수합계']],
               fill_color = 'YlGnBu',
               key_on = 'feature.id').add_to(map)

map
map = folium.Map(location=[36.2002, 127.054], zoom_start=7)
folium.Choropleth(geo_data = geo_str,
               data = pop_folium['소멸위기지역'],
               columns = [pop_folium.index, pop_folium['소멸위기지역']],
               fill_color = 'YlOrRd',
               key_on = 'feature.id').add_to(map)

map

 

 

 

 

 

 

 

 

 

- https://data.seoul.go.kr/

- 서울시 5대 점죄 발생현황 통계 데이터

- crime.txt

 

 

 

1. 데이터 가져오기

import pandas as pd
import folium
import json

criminal = pd.read_csv('./data/crime.txt', sep='\t', thousands=',', encoding='utf-8')

 

 

 

 

 

2. 데이터 전처리

 

# 불필요한 컬럼 제거

criminal.drop(['기간', '합계검거', '살인검거', '강도검거', '강간검거', '절도검거', '폭력검거'], axis=1, inplace=True)
criminal.drop([0], inplace=True)

 

 

# 컬럼 이름 설정

criminal.rename(columns={'살인발생':'살인', '강도발생':'강도', '강간발생':'강간', '절도발생':'절도', '폭력발생':'폭력'}, inplace=True)

 

 

# 인덱스 설정

criminal.set_index('자치구', inplace=True)

 

 

 

 

3. 단계 구분도

geo_path = './data/seoul.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))
map = folium.Map(location=[37.5502, 126.982], zoom_start=11,
                 tiles='Stamen Toner')
folium.Choropleth(geo_data = geo_str,
               data = criminal['살인'],
               columns = [criminal.index, criminal['살인']],
               fill_color = 'YlGnBu',
               key_on = 'feature.id').add_to(map)
map

 

 

 

 

 

4. 주소와 위도 경도 가져오기

# 경찰서 이름 만들기

station_name = []
for name in criminal.index:
    station_name.append('서울' + str(name[:-1]) + '경찰서')

print(station_name)
['서울종로경찰서', '서울중경찰서', '서울용산경찰서', '서울성동경찰서', '서울광진경찰서', '서울동대문경찰서', '서울중랑경찰서', '서울성북경찰서', '서울강북경찰서', '서울도봉경찰서', '서울노원경찰서', '서울은평경찰서', '서울서대문경찰서', '서울마포경찰서', '서울양천경찰서', '서울강서경찰서', '서울구로경찰서', '서울금천경찰서', '서울영등포경찰서', '서울동작경찰서', '서울관악경찰서', '서울서초경찰서', '서울강남경찰서', '서울송파경찰서', '서울강동경찰서']

 

 

# 카카오 지오코딩 API를 이용해서 경찰서 주소와 위도, 경도 찾아오기

import requests
import json
from urllib.parse import quote

#경찰서 주소와 위도, 경도 찾아오기
station_address = []
station_lat = []
station_lng = []
for name in station_name:
    q = quote(name)
    url = 'https://dapi.kakao.com/v2/local/search/keyword.json?query=' + q
    headers = {'Authorization': 'KakaoAK {}'.format('c454c0e64688ce2bde2dfff9cceced87')}
    data = requests.post(url, headers=headers)
    result = json.loads(data.text) #파싱한 결과
    documents = result["documents"]
    
    address = documents[0]["address_name"]
    x = documents[0]["x"]
    y = documents[0]["y"]
    
    station_address.append(address)
    station_lat.append(y)
    station_lng.append(x)
print(station_address)
print(station_lat)
print(station_lng)
['서울 종로구 공평동 1', '서울 강남구 역삼동 830-23', '서울 용산구 원효로1가 12-12', '서울 성동구 행당동 192-8', '서울 광진구 구의동 254-32', '서울 동대문구 청량리동 229', '서울 중랑구 신내동 810', '서울 성북구 삼선동5가 301', '서울 강북구 번동 415-15', '서울 도봉구 창동 17', '서울 노원구 하계동 250', '서울 은평구 불광동 산 24', '서울 서대문구 미근동 165', '서울 마포구 아현동 618-1', '서울 양천구 신정동 321', '서울 강서구 화곡동 980-27', '서울 구로구 구로동 3-25', '서울 금천구 시흥동 1030', '서울 영등포구 당산동3가 2-11', '서울 동작구 노량진동 72-35', '서울 관악구 봉천동 1695-5', '서울 서초구 서초동 1726-1', '서울 강남구 대치동 998', '서울 송파구 가락동 9', '서울 강동구 성내동 541-1']

['37.57185082446809', '37.49388566996322', '37.54117933635974', '37.5617947420747', '37.54292164557882', '37.5850804205685', '37.6182390801576', '37.58989767143614', '37.637406561393085', '37.65339041848567', '37.6423345266253', '37.6285242467996', '37.5649023404956', '37.550824477077796', '37.5165549067037', '37.5513470867979', '37.5070387936593', '37.45725314643252', '37.52585220719', '37.5131469479959', '37.4745875338795', '37.49561086789221', '37.5093899463715', '37.5019757297179', '37.52867274661258']

['126.98417036713045', '127.03221660594751', '126.96777437089538', '127.036472076258', '127.08396170505674', '127.045762797538', '127.10454224897', '127.01625465308534', '127.02733870040592', '127.05270598499145', '127.071404832413', '126.928631038584', '126.966790740735', '126.95400918908302', '126.865542970902', '126.849886071842', '126.890224925815', '126.89674921449215', '126.90094457988', '126.942857105642', '126.951062346192', '127.00528097622329', '127.067070541137', '127.12716129352492', '127.12688982770433']

 

 

 

 

 

 

5. 지도 출력

criminal['lat'] = station_lat
criminal['lng'] = station_lng
map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

for n in criminal.index:
    folium.Marker([criminal['lat'][n], 
                   criminal['lng'][n]]).add_to(map)
map

 

map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

for n in criminal.index:
    folium.CircleMarker([criminal['lat'][n], criminal['lng'][n]],
                        radius = criminal['폭력'][n]/100.0, 
                        color='#3186cc', fill_color='#3186cc', fill=True).add_to(map)
    
map
map = folium.Map(location=[37.5502, 126.982], zoom_start=11)

folium.Choropleth(geo_data = geo_str,
               data = criminal['살인'],
               columns = [criminal.index, criminal['살인']],
               fill_color = 'YlGnBu',
               key_on = 'feature.id').add_to(map)

for n in criminal.index:
    folium.CircleMarker([criminal['lat'][n], criminal['lng'][n]],
                        radius = criminal['폭력'][n]/100, 
                        color='#3186cc', fill_color='#3186cc', fill=True).add_to(map)
    
map

https://github.com/southkorea/southkorea-maps

한국 지리정보 사이트

 

 

 

1. 데이터 읽어오기

state_unemployment = 'data/US_Unemployment_Oct2012.csv'
state_data = pd.read_csv(state_unemployment)
print(state_data.head())
  State  Unemployment
0    AL           7.1
1    AK           6.8
2    AZ           8.1
3    AR           7.2
4    CA          10.1

 

 

 

 

2. 단계 구분도 출력

  • folium.Map으로 만든 지도에 choropleth 함수 호출
    • geo_data 옵션에 아이디와 위도 및 경도를 가진 json 파일의 경로를 설정해주고 data에 출력한 DF 설정
    • key_on에 json 파일에서 사용되는 id 컬럼 지정
state_geo = 'data/folium_us-states.json'

map = folium.Map(location=[40, -98], zoom_start=4)

folium.Choropleth(geo_data=state_geo, data=state_data,
             columns=['State', 'Unemployment'],
             key_on='feature.id',
             fill_color='YlGn',
             legend_name='Unemployment Rate (%)').add_to(map)
map

 

 

 

 

 

 

1. 서울시 자치구별 CCTV 현황 데이터 가져오기

- http://data.seoul.go.kr/

 

열린데이터광장 메인

데이터분류,데이터검색,데이터활용

data.seoul.go.kr

- CCTV 검색

- xlsx 파일을 다운로드(파일 이름을 cctv로 수정)

 

 

# 데이터 읽기

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from pandas import Series, DataFrame
import platform
from matplotlib import font_manager, rc

#데이터 읽어오기
cctv = pd.read_excel('./data/cctv.xlsx')
print(cctv.head())
cctv.info()


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 25 entries, 0 to 24
Data columns (total 10 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   기관명       25 non-null     object 
 1   소계        25 non-null     int64  
 2   2011년 이전  23 non-null     float64
 3   2012년     23 non-null     float64
 4   2013년     23 non-null     float64
 5   2014년     25 non-null     int64  
 6   2015년     25 non-null     int64  
 7   2016년     25 non-null     int64  
 8   2017년     25 non-null     int64  
 9   2018년     25 non-null     int64  
dtypes: float64(3), int64(6), object(1)
memory usage: 2.1+ KB

 

 

 

 

 

2. 서울시 자치구별 인구 현황 데이터 가져오기

- http://data.seoul.go.kr/

- 자치구별 인구 검색

- pop.txt

 

 

# 데이터 읽기

pop = pd.read_csv('./data/pop.txt',  encoding='utf-8', skiprows=2, delimiter='\t', thousands=',')
print(pop.head())
print()
pop.info()



<class 'pandas.core.frame.DataFrame'>
RangeIndex: 26 entries, 0 to 25
Data columns (total 14 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   기간        26 non-null     object 
 1   자치구       26 non-null     object 
 2   세대        26 non-null     int64  
 3   계         26 non-null     int64  
 4   남자        26 non-null     int64  
 5   여자        26 non-null     int64  
 6   계.1       26 non-null     int64  
 7   남자.1      26 non-null     int64  
 8   여자.1      26 non-null     int64  
 9   계.2       26 non-null     int64  
 10  남자.2      26 non-null     int64  
 11  여자.2      26 non-null     int64  
 12  세대당인구     26 non-null     float64
 13  65세이상고령자  26 non-null     int64  
dtypes: float64(1), int64(11), object(2)
memory usage: 3.0+ KB

 

 

 

 

 

3. 데이터 전처리

# 컬럼 이름 변경

cctv.rename(columns={cctv.columns[0] : '구별'}, inplace=True)
print(cctv.head())
print()

gu = []
for x in cctv['구별']:
    gu.append(x.replace(' ', ''))
cctv['구별'] = gu

pop.rename(columns={pop.columns[1] : '구별'}, inplace=True)
print(pop.head())




 

 

# 필터링

#pop에서 컬럼 추출
pop = pop[['기간', '구별', '계', '남자', '여자']]

#pop의 첫번째 행은 합계
#첫번째 행 제거
pop.drop([0], inplace=True)

#여성인구 비율을 알아보기 위해서 새로운 열 생성
pop['여성비율'] = pop['여자']/pop['계']*100
pop

 

 

# 병합

#구별 컬럼을 이용해서 2개의 frame을 합치기
df = pd.merge(cctv, pop, on='구별')

 

 

# 불필요한 컬럼 제거

del df['2011년 이전']
del df['2012년']
del df['2013년']
del df['2014년']
del df['2015년']
del df['2016년']
del df['2017년']
del df['기간']

 

 

# 인덱스 재설정

df.set_index('구별', inplace=True)
df

 

 

 

 

 

 

4. 시각화

font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
df['소계'].plot(kind='barh', grid=True, figsize=(10,10))
plt.show()
df['소계'].sort_values().plot(kind='barh', grid=True, figsize=(5,5))
plt.show()
df['cctv비율'] = df['소계']/df['계'] * 100
df['cctv비율'].sort_values().plot(kind='barh', grid=True, figsize=(5,5))
plt.show()

 

 

# 시각화 - 산포도

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

 

 

# 시각화 - 기울기와 y절편 구해서 라인 그리기

fp1 = np.polyfit(df['계'], df['소계'], 1)
f1 = np.poly1d(fp1)
fx = np.linspace(100000, 700000, 100)

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

 

 

#오차 표시

fp1 = np.polyfit(df['계'], df['소계'], 1)
f1 = np.poly1d(fp1)
fx = np.linspace(100000, 700000, 100)
df['오차'] = np.abs(df['소계'] - f1(df['계']))
plt.figure(figsize=(14,10))
plt.scatter(df['계'], df['소계'], c=df['오차'], s=50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')

for n in range(24):
    plt.text(df['계'][n]*1.02, df['소계'][n]*0.98,
             df.index[n], fontsize=12)

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

Keras 의 내장 데이터 세트

- 종류

  • boston housing: 보스톤 집값으로 회귀에 사용
  • cifar10: 이미지 분류 데이터로 종류가 10가지
  • cifar100: 이미지 분류 데이터로 종류가 100가지
  • mnist: 손 글씨 데이터
  • fashion mnist: 의류 데이터로 클래스가 10가지
  • imdb: 영화 데이터로 분류에 활용
  • reuters: 뉴스 토픽 - 자연어 분류

- load_data 라는 함수를 호출하면 훈련 데이터 튜플 과 테스트 데이터 튜플로 데이터를 리턴
- 각각의 튜플은 피처 와 레이블로 나뉘어져 있음

 

 

 

1. 데이터 가져오기

fashion_mnist = keras.datasets.fashion_mnist
(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()

print(X_train_full.shape) #28 * 28 에 해당하는 이미지 6만개
print(X_test.shape)
print(y_test.shape)
(60000, 28, 28)

(10000, 28, 28)

(10000,)

 

X_train_full[0] #0부터 255까지의 값으로 되어 있음
  • 딥러닝은 0 ~ 1 사이의 실수인 경우 학습을 더 잘하는 것으로 알려져 있음
  • 값을 변경하지 않아도 학습은 가능하지만 되도록이면 0 ~ 1 사이의 숫자로 변환해주는 것이 좋습니다.

 

 

 

2. 정규화 및 훈련/검증 데이터 분할

X_valid, X_train = X_train_full[:5000] / 255. , X_train_full[5000:] / 255.
y_valid, y_train = y_train_full[:5000], y_train_full[5000:]
X_test = X_test/255.

 

 

# 이미지 1개 출력

plt.imshow(X_valid[0], cmap='binary')

 

 

# 각 클래스의 실제 레이블을 배열로 생성

class_names = ["T-shirt/top", "Trouser", "Pullover", "Dress", "Coat",
              "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot"]
print(y_train[0])
print(class_names[y_train[0]])
plt.imshow(X_train[0], cmap='binary')
4
Coat


 

 

 

 

 

3. Flatten Layer

  • 데이터의 차원을 1차원으로 변경해주는 Layer
  • Dense Layer 는 입력받는 데이터의 차원이 1차원만 가능
    • Dense Layer는 입력 데이터의 차원을 1차원으로 변경해서 사용해야 합니다.
    • 이미지 데이터의 경우는 변환을 해서 사용해야 합니다.
  • Flatten Layer를 자동으로 1차원으로 변경을 수행합니다.
print(np.array([[1, 2], [3, 4]]).flatten())
[1 2 3 4]

 

 

 

- 활성화 함수

- ReLU: max(0, x)
- Leaky ReLU: max(x, 0.1x) - 이론적으로 기울기 소실이 일어날 수 있지만 실제로는 거의 일어나지 않음

 

 

 

 

 

4. 모델 생성

model = keras.models.Sequential()
  • Dense는 데이터의 차원을 1차원으로 입력을 받아야 합니다.
  • 이미지 데이터의 경우는 가로 * 세로 또는 가로 * 세로 * 채널의수 로 입력
  • Flatten 은 원본 이미지의 차원을 주면 1차원으로 수정을 합니다.
model.add(keras.layers.Flatten(input_shape=[28, 28])) #Input Layer
model.add(keras.layers.Dense(300, activation="relu")) #Hidden Layer
model.add(keras.layers.Dense(200, activation="relu")) #Hidden Layer
model.add(keras.layers.Dense(100, activation="relu")) #Hidden Layer
  • Output Layer의 units 는 출력의 개수가 되어야 합니다.
  • 분류 문제는 활성화 함수로 softmax 가 잘 동작합니다.
  • 출력을 하나로 만들고자 하면 sigmoid를 사용하면 됩니다.
model.add(keras.layers.Dense(10, activation="softmax")) #Output Layer

 

 

 

 

 

5. 모델 구조 확인

model.summary()

 

 

- 손실 함수

  • 출력층의 활성화 함수가 sigmoid: binary_crossentropy
  • 출력층의 활성화 함수가 softmax: 
    • categorical_crossentropy(원핫인코딩 된 경우)
    • sparse_categorical_crossentropy(원핫인코딩 안된 경우)

 

 

- 옵티마이저

  • 손실을 낮추기 위해서 신경망의 가중치 와 학습률 등의 신경망의 속성을 변경하는데 사용되는 최적화 방식
  • 종류
    • SGD(확률적 경사하강법)
    • RMSprop
    • Adam: 가장 많이 사용되는 알고리즘으로 좋은 성능을 내는 것으로 알려져 있음
    • Adadelta
    • Adagrad
    • Nadam
    • Ftrl

 

- 평가 지표

  • 분류: auc, precision, recall, accuracy
  • 회귀: mse, mae, rmse

 

 

 

 

6. 모델 complie

model.compile(optimizer='adam',
             loss='sparse_categorical_crossentropy', 
             metrics=['accuracy'])

# 모델 훈련
history = model.fit(X_train, y_train, epochs=30,
                   validation_data = (X_valid, y_valid))

 

 

# 일반화 오차 - 평가

model.evaluate(X_test, y_test)

 

 

# 예측

  • predict: 각 클래스에 대한 기댓값
  • predict_classes: 가장 높은 클래스가 리턴
X_new = X_test[:3]
y_proba = model.predict(X_new)
print(y_proba.round(3))

 

y_pred = np.argmax(model.predict(X_new), axis=-1)
print(y_pred)
print(np.array(class_names)[y_pred])

데이터 url

    red: http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv
    white: http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-

 

 

white.csv

- 구분자: ;

- 피처
    fixed acidity
    volatile acidity
    citric acid
    residual sugar
    chlorides
    free sulfur dioxide
    total sulfur dioxide
    density
    pH
    sulphates
    alcohol
    quality

 

 

 

1. 데이터 가져오기

import pandas as pd
red = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv',
                 sep=";")
red.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1599 entries, 0 to 1598
Data columns (total 12 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   fixed acidity         1599 non-null   float64
 1   volatile acidity      1599 non-null   float64
 2   citric acid           1599 non-null   float64
 3   residual sugar        1599 non-null   float64
 4   chlorides             1599 non-null   float64
 5   free sulfur dioxide   1599 non-null   float64
 6   total sulfur dioxide  1599 non-null   float64
 7   density               1599 non-null   float64
 8   pH                    1599 non-null   float64
 9   sulphates             1599 non-null   float64
 10  alcohol               1599 non-null   float64
 11  quality               1599 non-null   int64  
dtypes: float64(11), int64(1)
memory usage: 150.0 KB

 

white = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv',
                 sep=";")
white.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4898 entries, 0 to 4897
Data columns (total 12 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   fixed acidity         4898 non-null   float64
 1   volatile acidity      4898 non-null   float64
 2   citric acid           4898 non-null   float64
 3   residual sugar        4898 non-null   float64
 4   chlorides             4898 non-null   float64
 5   free sulfur dioxide   4898 non-null   float64
 6   total sulfur dioxide  4898 non-null   float64
 7   density               4898 non-null   float64
 8   pH                    4898 non-null   float64
 9   sulphates             4898 non-null   float64
 10  alcohol               4898 non-null   float64
 11  quality               4898 non-null   int64  
dtypes: float64(11), int64(1)
memory usage: 459.3 KB

 

 

 

2. 기술통계량 확인

wine.describe()


 

 

 

 

3. 정규화

- 데이터의 값을 0 ~ 1로 정규화

wine_norm = (wine - wine.min()) / (wine.max() - wine.min())
wine_norm.describe()

 

 

 

 

4. 데이터 셔플

#데이터를 랜덤하게 섞기
wine_shuffle = wine_norm.sample(frac=1)
wine_np = wine_shuffle.to_numpy()
print(wine_np[:5])
[[0.26446281 0.06       0.44578313 0.18711656 0.05980066 0.07986111
  0.2764977  0.13668787 0.41860465 0.08988764 0.60869565 0.83333333
  1.        ]
 [0.32231405 0.37333333 0.12650602 0.02453988 0.11295681 0.10763889
  0.29262673 0.16367843 0.42635659 0.12921348 0.27536232 0.33333333
  0.        ]
 [0.24793388 0.20666667 0.20481928 0.10429448 0.01827243 0.12847222
  0.29262673 0.09658762 0.35658915 0.12359551 0.57971014 0.66666667
  1.        ]
 [0.20661157 0.13333333 0.28313253 0.16257669 0.05149502 0.20833333
  0.4078341  0.1698477  0.31007752 0.16292135 0.2173913  0.5
  1.        ]
 [0.29752066 0.18       0.12048193 0.20398773 0.07475083 0.21527778
  0.51382488 0.22691344 0.30232558 0.15730337 0.13043478 0.5
  1.        ]]

 

 

 

 

5. 훈련 데이터와 테스트 데이터 만들기

#80%에 해당하는 인덱스 구하기
train_idx = int(len(wine_np) * 0.8)
#80% 기준으로 훈련 데이터 와 테스트 데이터 분리
train_X, train_Y = wine_np[:train_idx, :-1], wine_np[:train_idx, -1] 
test_X, test_Y = wine_np[train_idx:, :-1], wine_np[train_idx:, -1] 

#타겟을 원핫인코딩을 수행
train_Y = tf.keras.utils.to_categorical(train_Y, num_classes=2)
test_Y = tf.keras.utils.to_categorical(test_Y, num_classes=2)
print(train_Y[0])
[0. 1.]
  • 일반 머신러닝 알고리즘에서는 타겟을 원핫인코딩 하지 않음

 

 

 

6. 분류 모델 생성

- 마지막 층의 units 은 출력하는 데이터의 개수로 회귀는 1 분류는 2 이상
- 분류 문제의 activation 은 softmax를 많이 사용

model = tf.keras.Sequential([
    tf.keras.layers.Dense(units=48, activation='relu', input_shape=(12,)),
    tf.keras.layers.Dense(units=24, activation='relu'),
    tf.keras.layers.Dense(units=12, activation='relu'),
    tf.keras.layers.Dense(units=2, activation='softmax')
])

model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.07),
             loss='categorical_crossentropy',
             metrics=['accuracy'])
model.summary()
  • softmax 함수는 자연 로그의 밑인 e의 지수를 사용해 계산한 뒤 모두 더한 값으로 나누는데 이렇게 나온 결과는 총합이 1.0 인 확률값
  • softmax 는 분류 나 RNN 에서 다음 토큰 예측 등 결괏값으로 확률이 필요한 분야에서 사용
  • 이 경우는 [0.97, 0.03]  으로 나오는데 앞이 red 와인일 확률이고 1이 white 와인일 확률
  • 시그모이드 처럼 곡선 함수

 

#소프트맥스 함수

#소프트맥스 함수
import math

x = np.arange(-2, 2, 0.01)
e_x = math.e ** x

plt.plot(x, e_x)
plt.show()
  • entropy: 정보이론에서 정보량을 나타내기 위해 사용하는 단위
  • 확률의 역수에 로그를 취한 값
  • -log확률
  • 확률이 높은 사건일 수 록 정보량이 적다고 판단하기 때문
  • 엔트로피의 기댓값은 엔트로피에 확률을 곱해준 값
  • 엔트로피가 높은 것은 높은 불확실성을 나타냄
  • 분류문제에서 불확실성의 정도는 낮추는 방향으로 학습을 진행
  • 분류 문제에서는 어느 한쪽의 확률이 높은 쪽으로 학습을 진행

 

 

 

7. 훈련

  • 32개씩 가지고 학습
  • 25% 검증 데이터로 만들어서 확인
  • 총 25번 진행
history = model.fit(train_X, train_Y, epochs=25,
                   batch_size=32, validation_split=0.25)

 

 

 

 

8. 모델 평가

model.evaluate(test_X, test_Y)
  • 이진 분류를 할 때 타겟은 2개의 속성으로 만들어져야 하고 출력 층에서 unit의 개수는 2개이고 손실 함수로 categorical_crossentropy를 사용하고 출력 층의 activation 함수는 softmax를 사용
  • 다중 클래스 분류는 출력 층에서 unit 개수만 변경하면 됩니다.

 

 

 

9. 다중 클래스 분류

#타겟으로 사용할 만한 특성 확인

print(wine['quality'].describe())
count    6497.000000
mean        5.818378
std         0.873255
min         3.000000
25%         5.000000
50%         6.000000
75%         6.000000
max         9.000000
Name: quality, dtype: float64

 

 

#샘플의 타겟 비율이 5:1 정도가 넘어가면 샘플링 비율을 조정

print(wine['quality'].value_counts())
quality
6    2836
5    2138
7    1079
4     216
8     193
3      30
9       5
Name: count, dtype: int64

 

 

#6을 기준으로 6보다 작으면 0 6이면 1 7이상이면 2로 구간화

wine.loc[wine['quality'] <= 5, 'new_quality'] = 0
wine.loc[wine['quality'] == 6, 'new_quality'] = 1
wine.loc[wine['quality'] >= 7, 'new_quality'] = 2

print(wine['new_quality'].value_counts())
new_quality
1.0    2836
0.0    2384
2.0    1277
Name: count, dtype: int64

 

 

#불필요한 데이터 제거

del wine['quality']

 

 

#정규화

  • 딥러닝을 할 때는 이 작업을 하지 않아도 되는데 대신에  epoch 를 늘려주던지 아니면 layer을 더 많이 쌓아 해결
wine_backup = wine.copy()

wine_norm = (wine - wine.min()) / (wine.max() - wine.min())
wine_norm['new_quality'] = wine_backup['new_quality']

 

#셔플

  • 데이터를 읽을 때 순서대로 데이터를 읽었기 때문에 셔플을 하지 않으면 샘플링할 때 어느 한 쪽 데이터가 과표집(많이 표집) 될 수 있다.
  • 여론조사는 컨벤션 효과라고 하는 것 때문에 과표집 문제가 많이 발생
wine_shuffle = wine_norm.sample(frac=1)
print(type(wine_shuffle))
wine_np = wine_shuffle.to_numpy()
<class 'pandas.core.frame.DataFrame'>

 

train_idx = int(len(wine_np) * 0.8)
train_X, train_Y = wine_np[:train_idx, :-1], wine_np[:train_idx, -1] 
test_X, test_Y = wine_np[train_idx:, :-1], wine_np[train_idx:, -1]
  • 딥러닝을 이용해서 분류를 할 때는 타겟을 원핫인코딩 하는 경우가 많다.
  • 분류를 할 때는 activation 을 softmax로 설정하는 경우가 많은데 softmax는 각 클래스에 대한 기대값을 확률의 형태로 나타낸다.
  • 출력이 여러 개의 값으로 구성된다.
  • 가장 기대값이 높은 인덱스에 1을 설정하고 나머지는 0으로 설정
train_Y = tf.keras.utils.to_categorical(train_Y, num_classes=3)
test_Y = tf.keras.utils.to_categorical(test_Y, num_classes=3)

 

 

# 분류 모델 생성

  • 마지막 층의 units 은 출력하는 데이터의 개수로 회귀는 1 분류는 2 이상
  • 분류 문제의 activation 은 softmax를 많이 사용
  • 분류를 할 때 3가지 모양을 가져야 하므로 마지막 층의 units 만 3으로 수정
model = tf.keras.Sequential([
    tf.keras.layers.Dense(units=48, activation='relu', input_shape=(12,)),
    tf.keras.layers.Dense(units=24, activation='relu'),
    tf.keras.layers.Dense(units=12, activation='relu'),
    tf.keras.layers.Dense(units=6, activation='relu'),
    tf.keras.layers.Dense(units=3, activation='softmax')
])


model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.003),
             loss='categorical_crossentropy',
             metrics=['accuracy'])
model.summary()
model.fit(train_X, train_Y, epochs=25, batch_size=32, 
         validation_split=0.25)
model.evaluate(test_X, test_Y)