Data Traveler
Tantum videmus quantum scimus

2021-04-30

Author: 98hyun

Published: 2021-04-30

Tags: 시각화 개발환경 딥러닝 데이터분석 데이터베이스

Share :


  20210131 post update

먼저 20210131 post에 kakao api를 이용해 주소를 통한 위,경도 parsing 접근법이 틀려 update했다. update 내용 보러가기


  colab에서 kaggle api로 작업하기.

요즘 대부분 colab을 많이 애용한다. 사실 본인은 kaggle notebook을 많이 사용한다. 하지만, colab을 사용할 때도 있기 때문에 colab 에서 kaggle api를 통해서 데이터 분석을 진행하는 방법을 정리했다.

code


# terminal code
!pip install kaggle

from google.colab import files

uploaded = files.upload()

for fn in uploaded.keys():
  print('User uploaded file "{name}" with length {length} bytes'.format(
      name=fn, length=len(uploaded[fn])))

# Then move kaggle.json into the folder where the API expects to find it.
# 여기서 .파일들은 linux 서버에 의해서 숨김파일로 정의 된다. 
# 또, chmod 600으로 소유자 외에 읽고,쓰기,실행이 안된다. 소유자도 읽고,쓰기 밖에 안된다. 
# linux master를 공부하는데 기회가 될 때, 정리하면 괜찮을것 같다.
!mkdir -p ~/.kaggle/ && mv kaggle.json ~/.kaggle/ && chmod 600 ~/.kaggle/kaggle.json

# download
!kaggle competitions download -c sejong-ai-challenge-p2
!unzip sejong-ai-challenge-p2.zip

# read data 
import pandas as pd
pd.read_csv('/content/test.csv.zip')


  BERT 참고

구글이 풀어놓은 nlp 프로세싱인데 미리 훈련된 딥러닝 방법이다. 쉽게 말해서 가져다 쓰면 되는것이다. 위에 제목을 링크로 걸어놨다.

구글 번역기 키고 보면 2번 코딩 전까지 넉넉히 15분 걸린다.


  fasttext

bert와 같은 nlp관련 라이브러리다. fasttext는 facebook이 만든 nlp문제를 쉽게 다가올 수 있게 한 라이브러리다.

규칙만 잘 지키면, 간단하게 분류 모델을 만들수 있다.

문제는 캐글 소설 작가 분류 문제이고, 공부해본 내용이다. 참고

맛보기로 아래는 규칙을 코드로 표현했다.

code


... snip

# X_tr은 원래 데이터를 train과 valid으로 나눈 것 중 train이다.
for i,row in X_tr.iterrows():
    target=y_tr.loc[i]
    # target을 앞에 prefix로 붙여야한다. 규칙.  
    label=f'__label__{target}' 
    text=row['keyword']+' '+row['location']+' '+row['text']
    label+=' '+text
    tr_arr.append(label)

for i,row in X_val.iterrows(): 
    # train을 나눈것이니 label은 정해져있다. validation을 위해 나뒀다.  
    text=row['keyword']+' '+row['location']+' '+row['text']
    val_arr.append(text)

for i,row in test.iterrows():
    text=row['keyword']+' '+row['location']+' '+row['text']
    test_arr.append(text)

train_df=pd.DataFrame(tr_arr)
# 나중에 파일 경로를 입력해줘야해서 새로운 train파일을 만든것이다. 
# quotechar은 "" 쌍따옴표 이거 인용구 만들려고 쓴다. 없앤것이다. 
# index는 당연 false, header도 false 한다. 중요. quoting은 escapechar과 같이 쓰인다. 
# escapechar은 인용구안하고 구분 어떻게 할껀데? 이런 뜻이다. 안한다는 뜻이다.
train_df.to_csv('train.txt',index=False,sep=' ',header=False,quoting=csv.QUOTE_NONE,quotechar="",escapechar=" ")

# model
model=fasttext.train_supervised('train.txt',label_prefix='__label__',epoch=10)
print(model.labels,'are the labels or targets the model is predicting')

# 예측 예측한것의 label은 [__label__{pred}] 형식의 리스트안의 문자열로 나온다. 
# 그래서 0으로 리스트index로 접근하고 마지막 글자만 가져왔다. 
# 만약 문자열로 정의한다면 .split('__')[-1]으로 접근하면 된다. 
pred=[int(label[0][-1]) for label in model.predict(test_arr)[0]]
... endsnip 


  Clustering

랜덤으로 k개의 centroids를 정한다. 그리고 각각의 object에 대해서 centorids와의 거리들을 계산한 후, 가까운 centroid에 label을 입력한다. 그 후 그룹의 object들의 x,y좌표를 평균으로 새로운 x,y를 만든 후 같은 작업을 하면서 object들의 움직임이 멈추고 centroids가 바뀌지 않으면 알고리즘을 멈춘다.

inertia와 cluster는 trade-off 관계에 있기 때문에 elbow방법을 사용한다.

code

fig, ax = plt.subplots(figsize=(15,7))

clusters_range = [2,3,4,5,6,7,8,9,10,11,12,13,14]
inertias =[]

for c in clusters_range:
    kmeans = KMeans(n_clusters=c, random_state=0).fit(cluster_scaled)
    inertias.append(kmeans.inertia_)

plt.plot(clusters_range,inertias, '-' , color='#244747',alpha = 0.8,linewidth=8)
plt.plot(clusters_range,inertias, 'o',linewidth=20,color='#d4dddd')    


plt.xlabel('Number of Clusters',fontsize=12) , plt.ylabel('Inertia',fontsize=12)
ax.xaxis.set_ticks(np.arange(2,15,1))

# Title & Subtitle
fig.text(0.12,0.96,'Age, annual income and spending score', fontfamily='serif',fontsize=15, fontweight='bold')
fig.text(0.12,0.92,'We want to select a point where inertia is low, and the number of clusters is not overwhelming for the business.',fontfamily='serif',fontsize=12)


ax.annotate(" We'll select 6 clusters", 
            xy=(4.5, 100), fontsize=12,
            va = 'center', ha='center',
            color='#4a4a4a',
            bbox=dict(boxstyle='round', pad=0.4, facecolor='#efe8d1', linewidth=0))

# Grid
ax.set_axisbelow(True)# Ax spines
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(True)
ax.spines['left'].set_visible(True)
ax.spines['right'].set_visible(False)

ax.spines['left'].set_color('lightgray')
ax.spines['bottom'].set_color('lightgray')
ax.yaxis.grid(color='lightgray', linestyle='-')
plt.show()


  Silhouette score

clustering이 잘 됐는지 확인하는 대표적인 metric이다.

code

# Silhouette score 개념:

* 데이터에 대해서 cluster에 잘 모여있고 다른 cluster와 잘 분리되어 있는지 측정한다. 
* Silhouette score는 샘플과 동일한 클러스터 내의 다른 데이터 포인트 사이의 클러스터 내 거리 (a)와 샘플과 다음으로 가장 가까운 클러스터 사이의 클러스터 간 거리 (b)를 고려한다.
* The silhouette score는 범위는 [-1,1]에 속한다. 
* 1에 가까울수록 잘 분리됐고, 잘 모여있다는 뜻이고, 0이나 -1에 가까울 수록 겹치고, 흩어져있다는 뜻이다.
* silhouette plots은 k-means clustering에서 최적의 cluster 개수를 선택하는데 사용이 된다.
* silhouette plots에서 주의해야 할 측면은 평균 silhouette score보다 낮은 cluster 점수, cluster 크기의 광범위한 변동 및 silhouette 플롯의 두께다.

from sklearn.metrics import silhouette_samples, silhouette_score

import matplotlib.cm as cm

clusterer = KMeans(n_clusters=6, random_state=1)
cluster_labels = clusterer.fit_predict(cluster_scaled)
silhouette_avg = silhouette_score(cluster_scaled, cluster_labels)

# Compute the silhouette scores for each sample
sample_silhouette_values = silhouette_samples(cluster_scaled, cluster_labels)

fig, ax1 = plt.subplots(figsize=(10,6))

y_lower = 10
for i in range(6):
    # Aggregate the silhouette scores for samples belonging to
    # cluster i, and sort them
    ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
    ith_cluster_silhouette_values.sort()

    size_cluster_i = ith_cluster_silhouette_values.shape[0]
    y_upper = y_lower + size_cluster_i

    color = cm.nipy_spectral(float(i) / 6)
    ax1.fill_betweenx(np.arange(y_lower, y_upper),0, ith_cluster_silhouette_values, facecolor='#244747', edgecolor="black",linewidth=1, alpha=0.8)

    # Label the silhouette plots with their cluster numbers at the middle
    ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))

    # Compute the new y_lower for next plot
    y_lower = y_upper + 10  # 10 for the 0 samples


fig.text(0.198, .99, 'Clustering: Silhouette scores', fontsize=15, fontweight='bold', fontproperties=fontprop)
fig.text(0.198,.93, 'we want each cluster to look roughly the same - we acheive that here.', fontsize=12, fontweight='light', fontproperties=fontprop)


ax1.get_yaxis().set_ticks([])
#ax1.set_title("Silhouette plot for various clusters",loc='left')
ax1.set_xlabel("Silhouette Coefficient Values")
ax1.set_ylabel("Cluster label")
# The vertical line for average silhouette score of all the values
ax1.axvline(x=silhouette_avg, color="lightgray", linestyle="--")
ax1.set_xticks([0, 0.2, 0.4, 0.6, 0.8])
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
ax1.spines['left'].set_visible(False)

plt.show()


  Time Series EDA

시계열 데이터를 분석할 때 주의할 점을 코드로 적었다. 참고

code


# 0. 시간 변수 처리
# 모양에 따라 format 처리. 만약, 20210430이라면 "%Y%m%d"가 맞고, 30/04/2021이라면 "%d/%m/%Y".
df['date'] = pd.to_datetime(df['date'], format = '%Y%m%d') 

# 1. data visualization 
# 다른 독립변수와 시간변수간 분포 그래프. 

# 2. data preprocessing
# 2-1. 순서대로 되어있는지, 빈 구간은 없는지 확인.
df = df.sort_values(by='date')

df['delta'] = df['date'] - df['date'].shift(1)
print(df['delta'].sum(), df['delta'].count())

# 2-2. 결측값이 있는지. 
# 만약 있다면, 첫번째. 아예 -999로 채우거나 0으로 두기. 
# 평균이나 앞의 값으로 채우기. 혹은 주변값과 interpolate하기. 

# 2-3. resampling 
# 7D는 7Days를 의미한다. 15D, M은 month를 의미한다. 여러가지를 해보고 결정한다. 
df.resample('7D', on='date').mean().reset_index(drop=False)

# 2-4. stationary
# 3가지로 본다. visual, basic statistic, statistic test. 
# 그 중 통계방법은 adfuller로 귀무가설(단위근을 가지고, 비정상성이다.)과 대립가설(단위근이 없고, 정상성이다.)로 나눠서 검정한다. 
# 당연하지만, p-value(1종오류가 일어날 확률)가 유의수준보다 낮으면 귀무가설을 기각한다. 
# critical value 보다 adf-statistic 점수가 낮아도 귀무가설을 기각할 수 있다. 

# https://www.statsmodels.org/stable/generated/statsmodels.tsa.stattools.adfuller.html
from statsmodels.tsa.stattools import adfuller

result = adfuller(df['depth_to_groundwater'].values)

### result
(-2.880201649316658, ## adf-statistic 
 0.04769919092020916, ## p-value
 7, 
 592,
 {'1%': -3.441444394224128, ## critical value in 1%
  '5%': -2.8664345376276454, ## critical value in 5%
  '10%': -2.569376663737217}, ## critical value in 10%
 -734.3154255877625)

# 2-5. 필요에 따라 변환과 차분을 한다. 
# 후에 feature engineering 까지 한다. 

# 3. 이제 시계열 분해(decomposition)을 통해 4가지를 밝힌다. 
# Level, Trend, Seasonality, Noise 위 관계가 덧셈이면 additive 모델, 곱셈이면 multiplicative 모델. 

# 3-1. eda와 acf,pacf를 통해 모델의 방향을 결정한다. 

# 4. 모델링을 한다. 


  다익스트라 알고리즘

최단경로 알고리즘이다. heap 자료구조를 이용해서 개선된 다익스트라 알고리즘을 구현했다.
코드는 동빈나 알고리즘과 같다. '이것이 취업을 위한 코딩 테스트다'라는 책을 공부했다.
기초 알고리즘을 위주로 알려주고 실전 문제도 뒤에 같이 있다. 아예 모르는 사람이 접했을 때는 괜찮을것 같고, 만약 알고리즘 문제는 일단 많이 풀어본다는 얘기를 들었다면 유튜브로 간단하게 공부하고 문제 많이 푸는 것을 추천한다.

요즘 유튜브가 잘 되어 있다. 정보의 표준화라는 말이 나올 정도로 인터넷이 되는 모든 나라,인터넷을 사용할 줄 아는 모든 사람들은 많은 데이터를 접할 수 있다.

시작이 중요하다고 생각한다.

code

import heapq
import sys
input = sys.stdin.readline
INF = int(1e9) # 무한을 의미하는 값으로 10억을 설정

# 노드의 개수, 간선의 개수를 입력받기
n, m = map(int, input().split())
# 시작 노드 번호를 입력받기
start = int(input())
# 각 노드에 연결되어 있는 노드에 대한 정보를 담는 리스트를 만들기
graph = [[] for i in range(n + 1)]
# 최단 거리 테이블을 모두 무한으로 초기화
distance = [INF] * (n + 1)

# 모든 간선 정보를 입력받기
for _ in range(m):
    a, b, c = map(int, input().split())
    # a번 노드에서 b번 노드로 가는 비용이 c라는 의미
    graph[a].append((b, c))

def dijkstra(start):
    q = []
    # 시작 노드로 가기 위한 최단 경로는 0으로 설정하여, 큐에 삽입
    heapq.heappush(q, (0, start))
    distance[start] = 0
    while q: # 큐가 비어있지 않다면
        # 가장 최단 거리가 짧은 노드에 대한 정보 꺼내기
        dist, now = heapq.heappop(q)
        # 현재 노드가 이미 처리된 적이 있는 노드라면 무시
        if distance[now] < dist:
            continue
        # 현재 노드와 연결된 다른 인접한 노드들을 확인
        for i in graph[now]:
            cost = dist + i[1]
            # 현재 노드를 거쳐서, 다른 노드로 이동하는 거리가 더 짧은 경우
            if cost < distance[i[0]]:
                distance[i[0]] = cost
                heapq.heappush(q, (cost, i[0]))

# 다익스트라 알고리즘을 수행
dijkstra(start)

# 모든 노드로 가기 위한 최단 거리를 출력
for i in range(1, n + 1):
    # 도달할 수 없는 경우, 무한(INFINITY)이라고 출력
    if distance[i] == INF:
        print("INFINITY")
    # 도달할 수 있는 경우 거리를 출력
    else:
        print(distance[i])


  A* 알고리즘

8-puzzle(A 알고리즘)문제를 a 알고리즘으로 풀었다. heap 자료구조를 이용한 풀이였다.
또한, 클래스로 구현해서 알기 쉽게 해놓았다. 참고한 사이트 링크다.

python을 공부하면서 느낀 점은 잘 모르는 사람이 봤을 때도 쉽게 알아볼 수 있는 변수이름과 부호 사용이다. 관련된 내용인 pep8이다. 그 외로 사람들마다 개성적이지만 트렌드한 스타일들이 있다. 잘 사용해야 함을 느꼈다.

--- update ---

class 안에 def lt(self,other):return self.f()<other.f() 넣어줘야한다. 이상하게 코드 snip에 넣으면 화면이 깨진다.

code

# 8-puzzle(a* algorithm)

import heapq

class State:
    def __init__(self,board,goal,moves=0):
        self.board=board
        self.goal=goal
        self.moves=moves

    def new_board(self,i1,i2,moves):
        new_board=self.board[:]
        new_board[i1],new_board[i2]=new_board[i2],new_board[i1]
        return State(new_board,self.goal,moves)

    def swap(self,moves):
        result=[]
        i=self.board.index(0)
        if i not in [0,1,2]:
            result.append(self.new_board(i,i-3,moves))
        if i not in [0,3,6]:
            result.append(self.new_board(i,i-1,moves))
        if i not in [2,5,8]:
            result.append(self.new_board(i,i+1,moves))
        if i not in [6,7,8]:
            result.append(self.new_board(i,i+3,moves))
        return result

    def f(self):
        return self.g()+self.h()

    def g(self):
        return self.moves 

    def h(self):
        return sum([1 if self.board[i]!=self.goal[i] else 0 for i in range(9)])

    def __str__(self):
        return f'''
        ------------------
        f(n)={self.f()} | {self.board[:3]}
        g(n)={self.g()} | {self.board[3:6]}
        h(n)={self.h()} | {self.board[6:]}
        ------------------
        '''

puzzle=[2,8,3,
        1,6,4,
        7,0,5]

goal=[1,2,3,
      8,0,4,
      7,6,5]

open_queue=[]
heapq.heappush(open_queue,State(puzzle,goal))

solved_queue=[]
moves=0

while open_queue:
    current=heapq.heappop(open_queue)

    print(current)
    if current.board==goal:
        print("\t탐색성공")
        break

    moves=current.moves+1
    for state in current.swap(moves):
        if state not in solved_queue:
            heapq.heappush(open_queue,state)
        solved_queue.append(state)  


  SQL

sql을 공부하고 있다. 최근 공부한 두개의 프로젝트를 간단히 기록했다. sql syntax는 링크에서 공부할 수 있다.

code


# 1. sqlite3 
# sqlite3는 python에서 사용할 수 있는 library다.
# 실제 소방공공데이터 프로젝트에서 사용했던 코드다. 
# sqlite3로 만들어진 db는 로컬에 데이터를 저장해서 영구적으로 사용할 수 있다.
# 큰 사이즈가 아니라면 빠르게 서비스를 만들 수 있다.

import sqlite3

con=sqlite3.connect('fire.db')
cur=con.cursor()

# drop table
cur.execute("DROP TABLE accident")
print("Table dropped... ")
con.commit()

# Create table 1
cur.execute('''
CREATE TABLE fire 
(sidoNm text, 
flsrpPrcsMnb integer,
slfExtshMnb integer, 
fireRcptMnb integer, 
stnEndMnb integer, 
ocrnYmd text,
falsDclrMnb integer)
''')

con.commit()
con.close()

# 2. bigquery api
# 캐글에 간단하게 개념들을 배울 수 있는 course들이 있다. bigquery만 공부해봤다. [참고](https://www.kaggle.com/learn/intro-to-sql)  
# 구글에서 bigquery api를 배포했고, python으로 조작가능하게 했다. 
# 예제는 프로젝트는 public google analytics 데이터를 사용했다. 

# library 
from google.cloud import bigquery
client=bigquery.Client()
from bq_helper import BigQueryHelper

# basic code
query=\
"""
select * from `bigquery-public-data.google_analytics_sample.ga_sessions_20170801`
limit 10
"""
job=client.query(query)
df=job.to_dataframe()

# browse 
bqh=BigQueryHelper('bigquery-public-data','google_analytics_sample')
print(bqh.table_list()[:3]) # 3개 table 보기.
print(bqh.head('ga_sessions_20170801',num_rows=5)) # pandas head와 같은 역할. 

# {'index':...,'value':...,'top':...} 형태. 
query=\
"""
select * from unnest(array(select totals from `bigquery-public-data.google_analytics_sample.ga_sessions_*` # 와일드카드
where _table_suffix between '20170701' and '20170731'))
"""

# [{'index':...,'value':...}] 형태.
query=
"""
select param.index,param.value from `bigquery-public-data.google_analytics_sample.ga_sessions_*`
,unnest(customDimensions) as param 
-- param = alias. # --은 주석
where _table_suffix between '20170701' and '20170731'
"""


  DataBase

sql도 database에서 data를 가져오기 위해 특별히 사용되는 언어다.
database에서 중요한 ERD를 그려봤다. 예제는 사원과 부서를 그려봤고, draw.io에서 그렸다.
dbdiagram도 좋다.

중요한 개념이 너무나도 많다. entity, relationship 등..

요즘 sqld를 공부하고 있어서 중요한 내용을 업데이트를 할 생각이다.



  분류 목적함수

--update--

모델 평가도에서 사용해야 하는 기준들을 코드로 정리했다.

손실함수가 최소화/최대화 되는 목적함수 분류가 잘됐는지 확인하는 목적 함수를 잘 설정해야 모델이 학습을 잘 할 것이다.

아래는 테이블로 표현한 분류표이다.

실제
양성 음성
예측 양성 True Positive False Negative
1종 오류.
음성 False Negative
2종 오류.
True Negative
code


from sklearn.metrics import confusion_matrix

y_true=[0,0,0,1,1,1,1,1]
y_pred=[0,1,0,1,0,1,0,1]

tn,fp,fn,tp=confusion_matrix(y_true,y_pred).ravel()

## 특이도(specificity)
## 실제 음성 중 맞춘 음성의 수
spec=tn/(fp+tn)
## 민감도(sensitivity)
## 실제 양성 중 맞춘 양성의 수 
sens=tp/(tp+fn)
## 정밀도(precision)
## 양성이라고 예측한 것 중 맞춘 양성의 수
prec=tp/(tp+fp)
## 재현율(recall)
## 실제 양성 중 맞춘 양성의 수 (민감도와 같다.)
recall=tp/(tp+fn)

## 왜 이렇게 많은 기준이 필요할까? 
## 이해하기 쉬운 예를 들어 설명한다.
## 의사A는 환자B가 병에 걸렸는지 확인하기 위해서 예측을 했다. 
## 문제는 예측이 틀린 것이다. 이때, 진짜 병에 걸렸는데 안걸렸다고 하는게 위험한가, 병에 안걸렸는데 걸렸다고 하는것이 위험한가? 
## 여기서 실제 양성인데 음성이라고 잘못 판단하는 것을 2종오류
## 실제 음성인데 양성이라고 잘못 판단하는 것을 1종오류라고 한다. 
## 위의 정답은 1종오류보다 2종오류가 더 위험하다는 것이다.

## 분류하는 모델을 평가하기 위한 기준이 많은 이유는 특정 상황에 맞게 해야하기 때문이다.
## 위와 같이 병에 대해서 얘기할때에는 모델의 정확도가 높으면서 fp를 줄여야 하는것이다.

## 이를 보완하기 위해서 precision과 recall을 같이 봐야한다. 
## 정밀도를 높이면 재현율이 낮아지는, trade-off 관계에 있다. 
## 그래서 precision과 recall을 같이 보는 f1-score도 있다.(조화평균)


  lift 점수

예전에 공모전에서 공부했던 내용 중 lift 점수의 정의를 몰라 곤란했던 적이 있어서 정리했다.

lift 구현


# 보통 분류 문제에서 aoc와 함께 쓰이며 LIFT@20 이런식으로 많이 사용한다.
# 쉽게 설명하면 예측한 데이터 기준 상위 20% 데이터 중 1의 비율/전체 데이터에서의 1의 비율로 계산한다. 

import numpy as np
from sklearn.metrics import roc_curve,auc

def LIFT20(pred,true):
    # 상위 20%
    top20=np.argsort(pred)[:len(pred)//5] 
    top20_1=np.count_nonzero(top20==1) 
    true_1=np.count_nonzero(true==1)
    lift=(top20_1/len(top20))/(true_1/len(true))
    return lift

def auc(pred,true):
    # auc 구하기
    fpr,tpr,_=roc_curve(true,pred)
    auc=auc(fpr,tpr)
    return auc


Contact Form