https://www.acmicpc.net/problem/25371

 


 

k진수 정수의 자릿수 나누기

문제

양의 정수 n과 k가 주어진다. n을 k진수로 변환한 수를 a라고 하자. a의 각 자릿수를 0을 기준으로 나눈 결과를 집합 b라고 하자. 0이 연속으로 나와서 공백이 생기는 경우는 집합 b에 포함되지 않는다. 집합 b에 있는 수의 합을 k진수로 출력하자. 예를 들어, n = 19, k = 2이면 a = 100112, b = {1, 11}, 1 + 11 = 12, 12 = 11002이므로 1100을 출력한다.

입력

첫 번째 줄에 양의 정수 n과 k가 공백을 사이에 두고 순서대로 주어진다.

출력

첫 번째 줄에 집합 b에 있는 수의 합을 k진수로 출력한다.

제한

  • 1 ≤ n ≤ 1,000,000
  • 2 ≤ k ≤ 10

예제 입력 1 

437674 3

예제 출력 1 

22101

437674를 3진수 변환하면 a = 2110201010113이다.

2110201010113을 0을 기준으로 나누면 b = {211, 2, 1, 1, 11}이다.

b에 있는 수의 합은 211 + 2 + 1 + 1 + 11 = 226이다.

226을 3진수로 변환하면 221013이다.

예제 입력 2 

29 3

예제 출력 2 

10

29를 3진수 변환하면 a = 10023이다.

1002를 0을 기준으로 나누면 b = {1, 2}이다.

b에 있는 수의 합은 1 + 2 = 3이다.

3을 3진수로 변환하면 103이다.

예제 입력 3 

11 3

예제 출력 3 

10

11을 3진수 변환하면 a = 1023이다.

102를 0을 기준으로 나누면 b = {1, 2}이다.

b에 있는 수의 합은 1 + 2 = 3이다.

3을 3진수로 변환하면 103이다.

예제 입력 4 

3 3

예제 출력 4 

1

3을 3진수로 변환하면 a = 103이다.

10을 0을 기준으로 나누면 b = {1}이다.

b에 있는 수의 합은 1이다.

1을 3진수로 변환하면 13이다.

출처

알고리즘 분류

 


 풀이

 

사실 int(n,k)가 진수로 변경해주는 줄 알았는데 그런 건 아니어서 직접 함수로 구현해봤다.

 

  1. 값의 몫과 나머지를 구한다.
  2. 나머지는 리스트에 추가해둔다.
  3. 몫을 값으로 만들고 1,2번 과정을 n이 0보다 클 때까지 반복한다.
  4. 이후 리스트의 순서가 거꾸로이니 반대로 뒤집고 숫자로 만든다.

그리고 알고리즘 풀이 함수로 만들면 끝~

map에 lambda로 예외사항만 추가해주면 된다. 1002 숫자가 split이 되면 '1', '', '2'처럼 되는 예외사항을 추가해줬다.

# 진수 변경 함수 구현
def to_base(n,k):
    transes = []
    while n > 0:
        transes.append(str(n%k))
        n //= k
    transed_number = ''.join(transes[::-1])
    return int(transed_number)

# 알고리즘 풀이
def kbase_divide(n,k):
    # 타입 변경 후 k진수로 변환
    transed_number = to_base(n,k) 

    # 변환된 수 0을 기준으로 나눠서 리스트로 변환
    split_numbers_by_zero = str(transed_number).split('0')
    
    # 0이 남는 경우가 있음을 확인 lambda 형태로 수식 추가

    # 리스트 안에 값을 정수 형태로 바꾼 뒤 총합
    sum_split_numbers = sum(map(lambda x : int(x) if x != '' else 0 ,split_numbers_by_zero))

    # 다 합친 수를 k진수로 변환함
    transed_split_numbers = to_base(sum_split_numbers, k)

    print(transed_split_numbers)
    
if __name__ == "__main__":
    n, k = map(int,input().split())
    kbase_divide(n,k)

2.1 인간 행동의 기본 모델

행동 데이터 분석 ch2 p.51

 

인간 행동 모델 : 개인 특성 >> [인지와 감정 >> 의도  >> 개인의 행동] << (비즈니스 행동)

 

1. 개인 특성 (기여 요소)

 

    1. 인구통계 변수 : 연령, 성별 등

    2. 사회적 요소 : 직업, 가족관계 등

 

2. 인지와 감정

 

  1. 고객 만족도(Customer Satisfaction : CSAT)

  2. 고객 경험(Customer Expeerience : CX)

      - UX와의 차이 : UX는 인간이 무엇을 원하는 지, 어떻게 느끼는 지, 왜 그렇게 느끼는 지를 안다고 가정

      - 행동과학 : 인간이 자신의 머릿속에서 일어나는 많은 일을 인지하지 못한다고 가정

 

3. 의도

 

     : 의도-행동차이를 파악해야한다.

     - 수집 방법

        - 설문조사

        - 사용자 경험 관찰

4. 행동

    행동의 단계를 세분화해야 함

 

5. 비즈니스 행동

     데이터 수집의 문제점 

    1. 고객 행동을 세분화하는 수준만큼 비즈니스 행동을 상세하게 추적하지 못하는 조직
    2. 비즈니스 행동은 고객 행동과 관련된 변수를 해석하는  영향

 

2.2 행동과 데이터의 연결고리

1. 행동 무결성 사고방식

 

2. 불신과 검증

 

3. 범주 식별

 

4. 행동 변수 세분화

    1. 관찰 가능성

    2. 독립성

    3. 원자성

 

5. 맥락 이해

    1. 시간의 중요성

  • 빈도
  • 지속시간
  • 근접성
  • 사회적인 일상

    2. 정보와 '알려진 무지'

    3. 짖지 않는 개

    params = {
        'serviceKey': serviceKey,
        'pageNo': '1',        # 페이지 번호
        'numOfRows': '10',    # 한 번에 가져올 데이터 개수
        'resultType': 'json', # 응답 형식 (JSON)
        'sgId': '20200415',   # 선거 ID
        'sgTypecode': '2',    # 선거 구분 코드
        'cnddtId': '100135069', # 후보자 ID
    }
 

값은 당선인정보api에서 가져와 csv에 제대로 넣은 거 같은데 무엇이 문제일까?

11,20200415,2,100135069,성북구갑,서울특별시,성북구,1,,더불어민주당,김영배,金永培,남,19670308,53,서울특별시 성북구 고려대로2길,75,정당인,68,고려대학교 정치외교학과 졸업,"(전) 민선5기, 6기 성북구청장",(전) 문재인대통령 청와대비서관(정책조정/민정),82954,60.9

 



국회의원은 공약서가 있지 않았다....

이게 요청 메세지가 아닌 응답 메세지에 있어서 확인에 시간이 걸렸다 국회의원은 api로 확인할 수 없음을 확인하였다. 

어쩔 수 없이 공약 크롤링이 필요한 시점이 되었다.

https://policy.nec.go.kr

 

title

 

policy.nec.go.kr

 

우리위원회는 정당·후보자에게 ‘문자인식이 되는 PDF 파일’을 제출하여 시각장애인의 접근성·편의성을 확보토록 안내하고 있습니다

 

다행히 pdf에서 글자를 추출할 수 있다. 이제는 이름과 명칭을 연결해서 데이터를 만들어야 한다,,,

 

매우 귀찮아지긴 했지만, 그래도 할 수 있는 방향이 생겼다. 

https://do-one-more.tistory.com/7

 

공공 api로 국회의원 코드 읽어오기 - python

[STEP 1 ]우선 공공 api에 회원가입 후 선거 공약 정보를 활용 신청한다. 그다음 스크롤을 내리면 필수적으로 얻어야 하는 정보들을 볼 수 있다.추가로 얻어야 하는 params 정보는 선거ID, 선거 종류

do-one-more.tistory.com

 

 

저번에는 공공 api로 코드를 읽어 json 파일로 확인까지 했다

이번에는 json파일을 pandas를 이용해 df 형식으로 바꾼 뒤 CSV로 저장해줬다.

 

[STEP : 1] JSON파일을 df로 전환한 뒤, csv파일에 합치고 csv 파일로 저장하기

import requests
from dotenv import load_dotenv
import json
import os
import pandas as pd
        
def read_api_to_df(url,params):
    response = requests.get(url, params=params)
    
    if response.status_code == 200:
        print(f"API READ CORRECTLY {response.status_code}")
    else:
        print(f"Error Code :{response.status_code}")
    
    response_json = json.loads(response.content)
    json_to_df = pd.json_normalize(response_json)
    
    return json_to_df

def api_to_csv(url):
    
    df = pd.DataFrame()
    file_path = 'public_vote_code'
    
    for i in range(1,1001):
        params = {'serviceKey' : serviceKey, 'resultType' : 'json', 'pageNo' : f'{i}', 'numOfRows' : '100'}
        new_df = read_api_to_df(url,params=params)
        df = pd.concat([df, new_df])
        
    df.to_csv("public_vote_code.csv")
        
if __name__ == "__main__":    
    # api key
    load_dotenv()
    serviceKey = os.getenv("GONGGONG_API_KEY")
    
    # 공공데이터 국회의원 선거공약정보확인을 위한 코드 정보
    govcode_url = "http://apis.data.go.kr/9760000/CommonCodeService/getCommonSgCodeList"
    
    # api code csv로 저장
    api_to_csv(govcode_url)

완전히 이상한 결과가 나왔다.

json파일 안에 json 파일이 겹겹이 쌓인 형식이어서 값이 원하는 것과 다르게 나왔다.
그리고 데이터가 그렇게 많지도 않았다. response.body.items.item에 있는 값만 가져오도록 다시 만들어야 할 거 같다.

 

[STEP 2] json안에 있는 response.body.items.item만으로 csv파일 만들도록 하기

import requests
from dotenv import load_dotenv
import json
import os
import pandas as pd
        
def read_api_to_df(url,params):
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()  # HTTP 에러가 있으면 예외 발생
    except requests.exceptions.RequestException as e:
        print(f"Error during API request: {e}")
        return pd.DataFrame()

	# 이 부분에서 items까지 가져오도록 만듦
    try: 
        response_json = response.json()
        items = response_json.get('response', {}).get('body', {}).get('items', {}).get('item', [])
        print(items)

        if items:
            json_to_df = pd.json_normalize(items)
        else:
            print("No items found in response")
            return pd.DataFrame()
            
    except (KeyError, ValueError, TypeError) as e:
        print(f"Error during JSON processing: {e}")
        return pd.DataFrame()
    
    return json_to_df

def api_to_csv(url):
    
    df = pd.DataFrame()
    file_path = 'public_vote_code'
    
    for i in range(1,10):
        params = {'serviceKey' : serviceKey, 'resultType' : 'json', 'pageNo' : f'{i}', 'numOfRows' : '100'}
        new_df = read_api_to_df(url,params=params)
        df = pd.concat([df, new_df])
        
    df.to_csv("public_vote_code.csv")
        
if __name__ == "__main__":    
    # api key
    load_dotenv()
    serviceKey = os.getenv("GONGGONG_API_KEY")
    
    # 공공데이터 국회의원 선거공약정보확인을 위한 코드 정보
    govcode_url = "http://apis.data.go.kr/9760000/CommonCodeService/getCommonSgCodeList"
    
    # api code csv로 저장
    api_to_csv(govcode_url)

잘 들어온 모습을 확인할 수 있다.

[STEP 3] CSV에 있는 국회의원 선거 코드를 가져와서  국회의원 코드 가져오기

https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15000864

 

중앙선거관리위원회 당선인정보

당선인 정보를 조회 할 수 있는 서비스 이다. (선거종류, 선거구명, 시도명, 구시군명, 기호, 정당, 성명, 성별, 연령, 경력, 득표수, 득표율 등을 조회 가능)

www.data.go.kr

 

 

위에서 만든 csv에서 sgId를 list로 가져온 뒤, 당선인 정보를 가져온다.

import requests
from dotenv import load_dotenv
import json
import os
import pandas as pd

def get_votecode():
    try:
        csv_file = pd.read_csv('public_vote_code.csv')
        congress_vote_id_list = csv_file[csv_file['sgTypecode'] == 2]['sgId'].to_list()
        return congress_vote_id_list
    except FileNotFoundError:
        print("Error: 'public_vote_code.csv' not found.")
        return []

def read_api_to_df(url, params):
    try:
        response = requests.get(url, params=params, timeout=10)
        response.raise_for_status()
        print(f"Response status code: {response.status_code}")
    except requests.exceptions.RequestException as e:
        print(f"Error during API request: {e}")
        return pd.DataFrame()

    try:
        response_json = response.json()
        items = response_json.get('response', {}).get('body', {}).get('items', {}).get('item', [])
        if items:
            return pd.json_normalize(items)
        else:
            print("No items found in response")
            return pd.DataFrame()
    except json.JSONDecodeError as e:
        print(f"Error decoding JSON: {e}")
        return pd.DataFrame()
    except (KeyError, ValueError, TypeError) as e:
        print(f"Error during JSON processing: {e}")
        return pd.DataFrame()

def api_to_csv(url):
    serviceKey = load_api_key()
    
    df = pd.DataFrame()
    vote_code = get_votecode()
    if not vote_code:
        print("Error: No vote codes found.")
        return
    
    file_path = "vote_erection.csv"
    
    for id in vote_code:
        for i in range(1, 10):
            params = {
                'serviceKey': f'{serviceKey}',
                'pageNo': f'{i}',
                'resultType': 'json',
                'numOfRows': '100',
                'sgId': f'{id}',
                'sgTypecode': '2',
                'sdName': '',
                'sggName': ''
            }
            new_df = read_api_to_df(url, params=params)
            df = pd.concat([df, new_df], ignore_index=True)
    
    df.drop_duplicates(inplace=True)
    df.to_csv(file_path, index=False, encoding='utf-8-sig')
    print(f"Data saved to {file_path}")

def load_api_key():
    load_dotenv()
    serviceKey = os.getenv("GONGGONG_API_KEY")
    if not serviceKey:
        raise ValueError("API key not found in environment variables.")
    return serviceKey

if __name__ == "__main__":
    erection_url = 'http://apis.data.go.kr/9760000/WinnerInfoInqireService2/getWinnerInfoInqire'
    api_to_csv(erection_url)

 

[step 5] 국회의원 당선인 정보를 가지고 국회의원의 공약을 확인한다.

2개의 컬럼값을 가져오려면 DataFrame 형태로 가져오기에 to_list()가 되지 않고 to_dict로 가져와야한다.

그리고 각 레코드를 읽어서 저장하기 위해 to_dict('records')로 추가로 선정한다.

import requests
from dotenv import load_dotenv
import json
import os
import pandas as pd

def get_votecode():
    try:
        csv_file = pd.read_csv('vote_erection.csv')
        pledge_code_list = [(row['huboid'], row['sgId']) for row in csv_file[['huboid', 'sgId']].to_dict('records')]
        return pledge_code_list
    except FileNotFoundError:
        print("Error: 'vote_erection.csv' not found.")
        return []

def read_api_to_df(url, params):
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        print(f"Response status code: {response.status_code}")
    except requests.exceptions.RequestException as e:
        print(f"Error during API request: {e}")
        return pd.DataFrame()

    try:
        response_json = response.json()
        items = response_json.get('response', {}).get('body', {}).get('items', {}).get('item', [])
        if items:
            return pd.json_normalize(items)
        else:
            print("No items found in response")
            return pd.DataFrame()
    except json.JSONDecodeError as e:
        print(f"Error decoding JSON: {e}")
        return pd.DataFrame()
    except (KeyError, ValueError, TypeError) as e:
        print(f"Error during JSON processing: {e}")
        return pd.DataFrame()

def api_to_csv(url):
    serviceKey = load_api_key()
    
    df = pd.DataFrame()
    pledge_code_list = get_votecode()
    if not pledge_code_list:
        print("Error: No vote codes found.")
        return
    
    file_path = "vote_pledge.csv"
    
    for huboid,sgId in pledge_code_list:
        for i in range(1, 10):
            params = {
                'serviceKey': f'{serviceKey}',
                'pageNo': f'{i}',
                'resultType': 'json',
                'numOfRows': '100',
                'cnddtId' : f'{huboid}',
                'sgId': f'{sgId}',
                'sgTypecode': '2',
                'sdName': '',
                'sggName': ''
            }
            new_df = read_api_to_df(url, params=params)
            df = pd.concat([df, new_df], ignore_index=True)
    
    df.drop_duplicates(inplace=True)
    df.to_csv(file_path, index=False, encoding='utf-8-sig')
    print(f"Data saved to {file_path}")

def load_api_key():
    load_dotenv()
    serviceKey = os.getenv("GONGGONG_API_KEY")
    if not serviceKey:
        raise ValueError("API key not found in environment variables.")
    return serviceKey

if __name__ == "__main__":
    pledge_url = "http://apis.data.go.kr/9760000/ElecPrmsInfoInqireService/getCnddtElecPrmsInfoInqire"    
    api_to_csv(pledge_url)


트래픽 1000개가 생각보다 적기 때문에 날짜를 확인해서 특정날짜로 제한해야할 수도 있겠다

책 저자의 github repo

https://github.com/BuissonFlorent/BehavioralDataAnalysis

 

GitHub - BuissonFlorent/BehavioralDataAnalysis: Support files for the O'Reilly book "Behavioral Data Analysis with R and Python"

Support files for the O'Reilly book "Behavioral Data Analysis with R and Python" by Florent Buisson - BuissonFlorent/BehavioralDataAnalysis

github.com

 

블로그 작성자의 github repo

https://github.com/woongCat/behavior_data_anlysis

 

GitHub - woongCat/behavior_data_anlysis: 행동데이터분석을 확인한 책입니다.

행동데이터분석을 확인한 책입니다. Contribute to woongCat/behavior_data_anlysis development by creating an account on GitHub.

github.com

 

1장 인과 행동 프레임워크

  • 응용 분석의 핵심
    : 인간의 행동을 변화시키려면 무엇이 행동을 유발하는지 이해하기

ex) 왜 같은 제품을 보고도 일부 사람은 구매를 하지만, 다른 사람은 구매하지 않았을까요?

  • 인과-행동 프레임워크
    : 행동이 가장 우선시 되고 이를 인관관계 다이어그램과 데이터로 이해할 수 있습니다.

행동 데이터 분석 ch.1 31페이지

1-1. 인과관계 분석을 선택한 이유

기술과 예측만으로는 인간의 행동을 파악하기 어렵기 때문이다.

- 분석의 유형
    1. 기술 분석 : 데이터에 대한 설명을 제공
    2. 예측 분석 : "현재와 같은 조건이 지속된다면~는 무엇이 될까?" 또는 "아직 측정하지 않은 것 확인"
    3. 인과관계 분석 : "만약 이렇다면?" 또는 "다른 조건에서는 어떻게 될까?"
        가장 널리 알려진 인과관계 분석 도구 = A/B 테스트
- 인간은 복잡한 존재다
    1. 여러 요인에 영향을 받는다 : 하나의 변수가 갖는 예측력은 높지 않다.
    2. 상황에 따라 다르다 : 작은 변화로 환경이 달라지면 결과를 예측하기 어렵다.
    3. 가변적이다 : 매번 굉장히 다른 행동을 보인다.
    4. 혁신적이다 : 환경조건이 변하면 한 번도 하지 않은 행동을 할 수 있다.
    5. 전략적이다 : 일부러 속임수를 쓰기도 한다.

    +) 이를 공감하지 못하는 수학적인 독자들에게
        1. 보간법(interpolation) : 새로운 데이터를 구할 때 예측한 숫자를 관측한 숫자 사이의 있는 값으로 설정하는 방법
        2. 보외법(extrapolation) : 수학에서 원래의 관찰 범위를 넘어서서 다른 변수와의 관계에 기초하여 변수의 값을 추정하는 과정
        3. 차원의 저주 : 데이터 학습을 위해 차원이 증가하면서 학습데이터 수가 차원의 수보다 적어져 성능이 저하되는 현상

1-2. 회귀 분석을 방해하는 교란

- 회귀 분석의 용도
    1. 예측 분석 : 알 수 없는(주로 미래)의 값을 추정할 때 사용
    2. 인과관계 분석 : 종속변수와 독립변수와의 관계 자체가 중요. 상관계수가 독립변수의 인과 효과를 나타내는 좋은 지표

- 데이터
    위에 허브에서 가져오면 된다

- 상관관계가 인과관계가 아닌 이유

    온도와 아이스크림 판매량이 온도가 1도씩 올라갈 때 마다 1.171달러씩 늘어난다.
    유난히 더운 10월의 아이스크림을 추가 구매해도 매출이 예측값을 미치지 못할 수 있다.
    왜냐하면, 예측에 영향을 끼치는 결정적인 요인을 포함시키지 않았기 때문이다.

    여름방학에 학생들이 아이스크림을 많이 구매하러 가서 온도가 높은 기간에 매출이 높아지는 것이다.
    판매시점은 기온과 매출의 관계를 교란하는 교란변수인 것이다.
    교란 변수가 있다면 회귀모델의 계수를 상관관계로 해석해야한다.

- 날씨의 영향을 추가한다면

    둘다 적절한 값으로 추정하는 것을 확인할 수 있다
# 인과관계가 상관관계가 아닌 이유
print(ols("icecream_sales ~ temps", data=stand_data_df).fit().summary())

# 날짜의 영향을 추가한 모델
print(ols("icecream_sales ~ temps + summer_months", data=stand_data_df).fit().summary())

- 사공이 많으면 배가 산으로 간다 (다중공선성)

    그렇다면 변수를 더 많이 추가하면 어떨까? 
    기온 변수에 더해, 여름철 여부 변수, 아이스크림 매출 변수, 아이스 커피 매출 변수 3가지를 추가한다.

    temps와 iced_coffee_sales 높은 p-value를 갖게 되고 summer_months만 의미 있는 변수가 된다.
# 다중공선성
print(ols("icecream_sales ~ temps + summer_months + iced_coffee_sales",data=stand_data_df).fit().summary())

- 허위 상관관계 벅슨의 역설(Berkson’s paradox) = 해명 효과(explain-away effect)

    아이스크림을 구매한 사람 중 초콜릿 아이스크림을 구매한 사람, 바닐라 아이스크림을 구매한 사람의 인과관계를 보면 음의 상관관계가 나타난다.

    초콜릿 아이스크림을 구매한 사람은 바닐라 아이스크림이 싫어지거나 반대의 일도 일어나는 것일까?

    초콜릿 아이스크림을 구매한 사람이 바닐라 아이스크림까지 구매한 일이 없기 때문에 음의 상관관계가 일어난 것이다.
    초콜릿 맛을 좋아해서 아이스크림을 구매했다는 설명할 수 있지만, 초콜릿 맛을 좋아한다고 해서 바닐라 맛과의 상관관계는 구할 수 없다.

    애초에 이 데이터에는 두가지 맛 다 싫어하는 사람이 없기 때문이다.

    허위 상관관계가 발생한 것이다.

https://do-one-more.tistory.com/6

 

데이터 수집 추출 : Homebrew를 이용한 MYSQL 환경 구축 - 4장

먼저 BREW가 있다는 과정하에 글을 작성합니다.[STEP : 1 ] 맥에서는 brew install mysql을 통해 mysql을 설치할 수 있습니다.이걸 통해 설치 한 다음에 기본적인 설정을 해주면 됩니다.[STEP : 2] mysql_secure_ins

do-one-more.tistory.com

MYSQL에 데이터 베이스에서 만들었다면
MySQL workbench를 다운 받아서 좀 더 작업하기 쉽게 만들어보자!

brew install --cask mysqlworkbench

를 통해 mysql workbench를 받고 열어서 자신이 설정해서 mysql에 들어갔던데로 username과 password를 지정해주면 된다.

입력이 잘 된 모습~
높은 버전은 오류가 나는데 일단 가자
짜잔 클릭하면 들어올 수 있다!


전체추출을 해본다.

왼쪽 위에 스키마를 클릭하면 db이름을 볼 수 있다. 스키마는 DB와 동일하다고 생각하면 된다. 

그 이후에 아래 코드를 사용해서 전체추출을 해볼 수 있다. 

select * from pipeline_db.Orders;


증분추출을 해본다.

0. 증분 추출의 특징

- 추출 작업의 시간으로 데이터가 업데이트 된다면 시간

1. 증분 추출의 장점

- 증분 추출을 사용하면 업데이트 된 행을 더 쉽게 캡쳐할 수  있다.

2. 증분 추출의 단점

- 삭제된 행은 캡쳐되지 않는다.

- 원본 테이블에는 마지막으로 업데이트 된 시간에 대한 신뢰할 수 있는 타임스탬프가 있어야 한다.
- 그러기 위해서 업데이트가 된다면 업데이트 된 날짜로 데이터를 하나 더 추가해야한다.

SELECT *
FROM pipeline_db.Orders
WHERE LastUpdated > {{ last_extraction_run} };

가장 최신의 데이터인 2020-07-12:00:00 으로 날짜를 정했다.


vsCode를 이용해서 mysql에 연결해보자!

먼저 mysql과 연동할 수 있도록 하는 라이브러리를 install 해줘야 한다.

pip install pymysql

 

그 다음에 pipeline.conf라는 파일에 아까 connection을 만든 것처럼 연결 정보를 저장해야한다.

 

import pymysql
import csv
import boto3
import configparser

# mysql에 접속하기 위한 정보 받아오기
parser = configparser.ConfigParser()
parser.read("pipeline.conf")
HOSTNAME = parser.get("mysql_config", "HOSTNAME")
PORT = parser.get("mysql_config", "PORT")
DB_USER_NAME = parser.get("mysql_config", "DB_USER_NAME")
DB_PASSWORD = parser.get("mysql_config", "DB_PASSWORD")
DATABASE = parser.get("mysql_config", "DATABASE")

# mysql에 연결하기
conn = pymysql.connect(
host=HOSTNAME,
user=DB_USER_NAME,
password=DB_PASSWORD,
db=DATABASE,
port=int(PORT)
)

if conn is None:
print("Error connecting to the MySql database")
else:
print("MySQL connection established")
그럼 established 라고 나오면 접근하게 되었다.

전체추출을 해보고 CSV 파일을 S3에 올려보자

import pymysql
import csv
import boto3
import configparser

# mysql에 접속하기 위한 정보 받아오기
parser = configparser.ConfigParser()
parser.read("pipeline.conf")
HOSTNAME = parser.get("mysql_config", "HOSTNAME")
PORT = parser.get("mysql_config", "PORT")
DB_USER_NAME = parser.get("mysql_config", "DB_USER_NAME")
DB_PASSWORD = parser.get("mysql_config", "DB_PASSWORD")
DATABASE = parser.get("mysql_config", "DATABASE")

# mysql에 연결하기
conn = pymysql.connect(
host=HOSTNAME,
user=DB_USER_NAME,
password=DB_PASSWORD,
db=DATABASE,
port=int(PORT)
)

if conn is None:
print("Error connecting to the MySql database")
else:
print("MySQL connection established")

# 쿼리문 작성
m_query = "SELECT * FROM Orders;"

# 추출할 파일 이름
local_filename = "order_extract.csv"

# mysql에 연결 후 추출해서 전체 가져오기
m_cursor = conn.cursor()
m_cursor.execute(m_query)
results = m_cursor.fetchall()

# csv파일로 작성하기
with open(local_filename, 'w') as fp:
csv_w = csv.writer(fp, delimiter=',')
csv_w.writerows(results)

# 연 파일들 종료하기
fp.close()
m_cursor.close()
conn.close()

# S3에 접속하기 위한 정보 받아오기
parser = configparser.ConfigParser()
parser.read("pipeline.conf")
ACCESS_KEY = parser.get("aws_s3_config", "ACCESS_KEY")
SECRET_KEY = parser.get("aws_s3_config", "SECRET_KEY")
BUCKET_NAME = parser.get("aws_s3_config", "BUCKET_NAME")

# S3에 연결
s3 = boto3.client(
's3',
aws_access_key_id = ACCESS_KEY,
aws_secret_access_key = SECRET_KEY
)

s3_file = local_filename

# S3에 업로드하기
s3.upload_file(local_filename, BUCKET_NAME, s3_file)


데이터를 제대로 읽어온 모습을 볼 수 있다.
s3에 파일이 올라간 모습을 확인할 수 있다

 

https://github.com/woongCat/data_pipeline_guide

 

GitHub - woongCat/data_pipeline_guide: 데이터파이프라인 핵심 가이드 책을 따라해본 hub

데이터파이프라인 핵심 가이드 책을 따라해본 hub. Contribute to woongCat/data_pipeline_guide development by creating an account on GitHub.

github.com

 

[STEP 1 ]우선 공공 api에 회원가입 후 선거 공약 정보를 활용 신청한다. 

그다음 스크롤을 내리면 필수적으로 얻어야 하는 정보들을 볼 수 있다.

추가로 얻어야 하는 params 정보는 선거ID, 선거 종류 코드 후보자 ID

왜 필수 데이터를 어디서 받아와야 하는지 항목 설명에 적지 않았지?

 

[STEP : 2] 중앙선거 관리위원회 코드정보 데이터를 받아온다.

 

 

[STEP : 3]api를 통해 내용을 읽어오는 코드를 확인한다.

 

나는 dotenv를 통해서 .env에 인증키를 넣고 load_dotenv()로 읽어오는 방식을 사용해서 데이터를 숨겨서 가져왔다.

params에 들어가는 데이터는 decoding된 암호키를 읽어와야 request를 받을 수 있다.

 

이를 통해서 json 키를 읽어올 수 있다.

import requests
from dotenv import load_dotenv
import os

def api_test(url,params):
response = requests.get(url, params=params)

if response.status_code == 200:
print(response.content)
else:
print(f"Error Code :{response.status_code}")

if __name__ == "__main__":
 
load_dotenv()
serviceKey = os.getenv("GONGGONG_API_KEY")
 
# 공공데이터 국회의원 선거공약정보
pledge_params ={'serviceKey' : serviceKey, 'resultType' : 'json', 'pageNo' : '1', 'numOfRows' : '10', 'sgId' : '20231011', 'sgTypecode' : '4', 'cnddtId' : '1000000000' }
 
# 공공데이터 국회의원 선거공약정보확인을 위한 코드 정보
govcode_params = {'serviceKey' : serviceKey, 'resultType' : 'json'}
 
api_test(govcode_url,params=govcode_params)


 

이걸 통해 print를 확인하면 

 

b'{"response":{"header":{"resultCode":"INFO-00","resultMsg":"NORMAL SERVICE"},"body":{"items":{"item":[{"num":"1","sgId":"19920324","sgName":"\xec\xa0\x9c14\xeb\x8c\x80 \xea\xb5\xad\xed\x9a\x8c\xec\x9d\x98\xec\x9b\x90\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"0","sgVotedate":"19920324"},{"num":"2","sgId":"19920324","sgName":"\xea\xb5\xad\xed\x9a\x8c\xec\x9d\x98\xec\x9b\x90\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"2","sgVotedate":"19920324"},{"num":"3","sgId":"19921218","sgName":"\xec\xa0\x9c14\xeb\x8c\x80 \xeb\x8c\x80\xed\x86\xb5\xeb\xa0\xb9\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"0","sgVotedate":"19921218"},{"num":"4","sgId":"19921218","sgName":"\xeb\x8c\x80\xed\x86\xb5\xeb\xa0\xb9\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"1","sgVotedate":"19921218"},{"num":"5","sgId":"19960411","sgName":"\xec\xa0\x9c15\xeb\x8c\x80 \xea\xb5\xad\xed\x9a\x8c\xec\x9d\x98\xec\x9b\x90\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"0","sgVotedate":"19960411"},{"num":"6","sgId":"19960411","sgName":"\xea\xb5\xad\xed\x9a\x8c\xec\x9d\x98\xec\x9b\x90\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"2","sgVotedate":"19960411"},{"num":"7","sgId":"19971218","sgName":"\xec\xa0\x9c15\xeb\x8c\x80 \xeb\x8c\x80\xed\x86\xb5\xeb\xa0\xb9\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"0","sgVotedate":"19971218"},{"num":"8","sgId":"19971218","sgName":"\xeb\x8c\x80\xed\x86\xb5\xeb\xa0\xb9\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"1","sgVotedate":"19971218"},{"num":"9","sgId":"20000413","sgName":"\xec\xa0\x9c16\xeb\x8c\x80 \xea\xb5\xad\xed\x9a\x8c\xec\x9d\x98\xec\x9b\x90\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"0","sgVotedate":"20000413"},{"num":"10","sgId":"20000413","sgName":"\xea\xb5\xad\xed\x9a\x8c\xec\x9d\x98\xec\x9b\x90\xec\x84\xa0\xea\xb1\xb0","sgTypecode":"2","sgVotedate":"20000413"}]},"numOfRows":10,"pageNo":1,"totalCount":179}}}'

 

위와 같은 읽을 수 없는 b로 시작하는 바이너리 코드를 만날 수 있다. 이를 UTF-8로 디코딩 하거나 JSON으로 파싱하면 확인할 수 있다.

[STEP : 4] request한 데이터를 json 형태의 양식으로 확인하게 하는 코드를 작성한다.

import requests
from dotenv import load_dotenv
import json
import os

def api_test(url,params):
response = requests.get(url, params=params)

if response.status_code == 200:
print(json.loads(response.content))
else:
print(f"Error Code :{response.status_code}")

if __name__ == "__main__":
 
load_dotenv()
serviceKey = os.getenv("GONGGONG_API_KEY")
 
# 공공데이터 국회의원 선거공약정보
pledge_params ={'serviceKey' : serviceKey, 'resultType' : 'json', 'pageNo' : '1', 'numOfRows' : '10', 'sgId' : '20231011', 'sgTypecode' : '4', 'cnddtId' : '1000000000' }
 
# 공공데이터 국회의원 선거공약정보확인을 위한 코드 정보
govcode_params = {'serviceKey' : serviceKey, 'resultType' : 'json'}
 
api_test(govcode_url,params=govcode_params)

 

{'response': {'header': {'resultCode': 'INFO-00', 'resultMsg': 'NORMAL SERVICE'}, 'body': {'items': {'item': [{'num': '1', 'sgId': '19920324', 'sgName': '제14대 국회의원선거', 'sgTypecode': '0', 'sgVotedate': '19920324'}, {'num': '2', 'sgId': '19920324', 'sgName': '국회의원선거', 'sgTypecode': '2', 'sgVotedate': '19920324'}, {'num': '3', 'sgId': '19921218', 'sgName': '제14대 대통령선거', 'sgTypecode': '0', 'sgVotedate': '19921218'}, {'num': '4', 'sgId': '19921218', 'sgName': '대통령선거', 'sgTypecode': '1', 'sgVotedate': '19921218'}, {'num': '5', 'sgId': '19960411', 'sgName': '제15대 국회의원선거', 'sgTypecode': '0', 'sgVotedate': '19960411'}, {'num': '6', 'sgId': '19960411', 'sgName': '국회의원선거', 'sgTypecode': '2', 'sgVotedate': '19960411'}, {'num': '7', 'sgId': '19971218', 'sgName': '제15대 대통령선거', 'sgTypecode': '0', 'sgVotedate': '19971218'}, {'num': '8', 'sgId': '19971218', 'sgName': '대통령선거', 'sgTypecode': '1', 'sgVotedate': '19971218'}, {'num': '9', 'sgId': '20000413', 'sgName': '제16대 국회의원선거', 'sgTypecode': '0', 'sgVotedate': '20000413'}, {'num': '10', 'sgId': '20000413', 'sgName': '국회의원선거', 'sgTypecode': '2', 'sgVotedate': '20000413'}]}, 'numOfRows': 10, 'pageNo': 1, 'totalCount': 179}}}

 

원하는 결과가 나왔다.

 

이제 이 코드를 확인해서 데이터를 전처리 하고 당선인 정보를 확인한 뒤 선거 공약 정보를 추가할 수 있게 되었다.

 


결론 

공공 api를 request 하기 위해서는 params의 값을 추가해줘야 한다.

 

그 때 공공키는 디코딩된 암호를 추가해야 하고 받은 request는 바이너리 형태로 오기 때문에 json.load를 하거나 utf-8로 디코딩 해야 되는데 json 형식으로 저장하기 위해 json.load로 데이터를 저장하면 된다.

 

추가로 json으로 저장하는 코드를 작성하고 데이터를 확인해서 다른 api에 어떤 param으로 들어가야 하는지 추가 작업을 하면 될 거 같다.

먼저 BREW가 있다는 과정하에 글을 작성합니다.


[STEP : 1 ] 맥에서는 brew install mysql을 통해 mysql을 설치할 수 있습니다.

이걸 통해 설치 한 다음에 기본적인 설정을 해주면 됩니다.


[STEP : 2] mysql_secure_installation을 쉘에 입력한다!

[STEP : 3] 이후 나오는 문구를 읽어보고 Y일지 no일지 선택하면 된다.

1. setup VALIDATE PASSWORD component?

비밀번호 복잡도 검사를 적용할 것인지 설정하는 과정입니다. 복잡도 검사를 적용하게 된다면 다음과 같은 비밀번호 조건이 붙습니다.
- 최소 8자리 이상의 비밀번호
- 최소 1개의 영문자
- 최소 1개의 특수문자
- 최소 1개의 숫자

2. Remove anonymous users?

3. Disallow root login remotely?

4. Remove test database and access to it?

5. Reload privilege tables now?

 

[STEP : 4] mysql -u root -p로 지정한 비밀번호로 로그인 할 수 있게 되었다~

[STEP : 5] 새로운 DB를 생성 해준다.

CREATE DATABASE pipeline_db default CHARACTER SET UTF8

 

SHOW DATABASES; 로 확인 해준다.

[STEP : 6] 새로운 DB에 들어갈 유저를 생성 해준다.

CREATE USER 'pipeline_user'@'localhost' IDENTIFIED BY 'pipeline1';

[STEP : 7] 생성된 유저에게 DB에 들어갈 수 있는 권한을 부여해준다.

GRANT ALL PRIVILEGES ON pipeline_db.* TO 'pipeline_user'@'localhost';

[STEP : 8] exit;으로 콘솔 밖으로 나간 뒤 다시 로그인 해준다.

-u pipeline_user -p

지정된 데이터베이스만 보이는 모습

[STEP : 9]  USE pipeline_db를 하고 create table을 해준다.

USE pipeline_db;

CREATE TABLE Orders(
    OrderId int,
    OrderStatus varchar(30),
    LastUpdated timestamp);

create table

[STEP : 10]  INSERT INTO를 통해 값들을 테이블에 넣어준다.

[STEP : 11]  SELECT  *를 통해 값들을 전체 추출한다.

다 잘 들어가있는 모습이다.

+ Recent posts