낼름낼름 동동이

[ASAC 9일차] 파이썬 + SQL 기초 본문

데이터분석/파이썬

[ASAC 9일차] 파이썬 + SQL 기초

인죠인간 2024. 4. 2. 18:02

4월 2일의 기록


오늘 오전에는 SQL과 코딩 테스트 책을 받았다. 책을 받았지만 이런 강의 관련 책을 이용한 적이 많았던가..? 항상 구글 검색과 chatgpt를 통한 검색이 원하는 정보를 찾기 편리해 자주 이용하게 되는 것 같다.

아날로그 책만이 주는 감성은 이런 강의 책에서는 느끼기 어려운 것 같다.. 정보를 찾는 것에는 당연하게도 인터넷이 더 빠르고 확실하고 다양하니까

 

0. 목차


  1. 구현_카카오2022_주차요금문제 복습
  2. 음료수 얼려먹기 (Stack과 Queue 둘 다 가능)
  3. MySQL 설치 및 예제 몇가지

1. 구현_카카오2022_주차요금문제 복습


참고) https://dongdong-hoon.tistory.com/47

 

[Programmers][stack] 주차 요금 계산

stack 알고리즘을 더욱 이해하기 위해 점심 시간 이후 1시부터 2시 40분까지 문제를 생각해보며 풀어보았다. 기존의 다른 문제들은 stack을 1개의 리스트로 저장해서 사용해야 한다는 생각으로 접근

dongdong-hoon.tistory.com

위 글에서 정리했었지만, 다시 한 번 처음부터 코드를 따라가보면서 생각의 흐름을 정리해보았다.

정답 코드

  • 시간 변환 함수를 써서 요금 계산을 더욱 편하게 했던 점이 좋다.
  • out_time_list와 in_time_list를 구분해서 나간 시간과 들어온 시간을 각각 더해서 out_time_list- in_time_list로 만들어 가독성이 좋게 만든 점도 좋아보였다.
# 1) 시간 변환 함수
#   입력) HH:MM
#   할일) 입력HH:MM---> 00:00기준으로 지난간 분으로 계산
#   출력) 지난간 분
#   03:24 ---> :분리 --> 앞 시간*60 + 뒤 분
#   뒤에서 요금 계산의 편의성으로 숫자형으로 출력...

def convert_h_to_m(time_HHMM):
    h_part, m_part = time_HHMM.split(":")
    total_min = int(h_part) * 60 + int(m_part)
    return total_min

def solution(fees, records):
    answer = []

    # 1) 요금 계산에 대한 처리
    base_time, base_cost, add_time, add_cost = fees
    # 참고) 변수를 안하고 fees[0], fees[1]로 쓸수도 있다.
    # 근데 위처럼 쓰면 이해하기 더 좋을듯

    # 2) 입출에 대한 정보 처리
    # --> 처리된 정보를 어떻게 정리할지? : dict
    # key : 자동차 번호
    # value : (HH:MM 을 분으로 변경, 상태)
    # --> (5981 : [5*60 + 30 , IN])
    # --> (5981 : [334, IN], [360, out])

    park_dict = {}

    for record in records:
        part_time, car_id, status = records.splits(" ")

        # ["01:23", "0000", "IN"]
        # 정리의 기준 차량 번호 : key --> 숫자( "0000"--> int 0)
        # 마지막에 차량 번호 순으로 정렬되니까 이를 미리 숫자로 지정해서
        # 쉽게 정렬 할 수 있게 만들었다.
        car_id = int(car_id)

        # value에 들어갈 값을 정리하자

        car_record = [ convert_h_to_m(part_time),status]
        # 시간을 미리 convert 해서 관리

        # case1) 이미 한번 등록했던 차 번호 : key 기 등록
        if car_id in park_dict:
            park_dict[car_id].append(car_record)
            # {0:[ [83,"In"], [90,"out"] ]}
        
        # case2) 신규 입차한 차 번호 : key 새롭게 등록
        else:
            # {0:[ [90,"out"] ] }
            park_dict[car_id] = [car_record]
            # ==> value에 여러 출차 정보들을 담아
            #     value 양식을 리스트로 하고,
            #     개별 정보도 시간, 상태 2개 정보여서 리스트도 담을
        
    # 3) 누적 시간 + 비용을 계산을 하려고 함!!!!
    #   ==> 계산용에 대한 dict ( key 차번, value : 요금 전체)
    #   Check1) 출차 정보 처리 : 출차 정보 없으면 추가
    #   Check2) 머문 총 시간 계산
    #           2-1) 기본 요금 이하
    #           2-2) 기본 요금 초과
    #                     - 나누어 떨어질 때 : 몫 그대로
    #                     - 나누어 안 떨어질 떄 : 몫 + 1
    #                     ==> - 써서 하는 방법, math 패키지,
    # --> 2번에 차량별로 정리된 입출정보를 바탕으로 계산을
    
    car_dict = {}
    for car, logs in park_dict.items():
        # 개별 차에 대한 정보 처리
        # logs = [ [83,"In"], [90,"out"],[100,"In"] ]
        # 파이썬을 활용해서 리스트 컴프리해션으로,,,
        # ==> In : [83, 100]
        # ==> out  [ 90,]   
        out_time_list = [ m for m, sta in logs if sta=="OUT"]
        in_time_list = [m for m, sta in logs if sta =="IN"]

        if len(logs) % 2 == 1: #출차 정보가 없는 기록들.. 즉, 그날 쭉 있는 차
            out_time_list.append(convert_h_to_m("23:59"))
        
        # ==> 출차 정보 없는 경우 이렇게 처리를 할 것이다.
        # 차량별로 총 머문 시간을 계산(누적 주차 시간)
        total_min = sum(out_time_list) - sum(in_time_list)

        # 누적시간에 대한 요금 계산!!!!
        if total_min <= base_time:
            # 기본 시간 이내 누적 주차 : 기본 요금
            car_dict[car] = base_cost
        else: # 추가 요금 발생!!!!!
            # - 부호를 활용하는 방안 or 나눠떨어질떄와 아닐때 구별
            cost = 0
            cost += base_cost
            total_min -= base_time # 누적시간 =
            #올림에 대한 부호처리 테크닉을 사용한다.
            over_time_base = -(total_min * (-1)// add_time)

            cost += over_time_base * add_cost
            # 최종 차량별 요금 정리
            car_dict[car] =cost

    # 4) 정답지 출력요
    #    -> 정렬 : key에 차량 번호 오름차순
    #    -> 출력 정보 : 요금 value만 추려서 리스트로...
    #    ==> +++ 차량 번호에 대해서 숫자로 처리를 미리하고 처리를....
    car_cost = dict(sorted(car.dict.items())) # key 정렬
    answer = list(car_cost.values())

2. 음료수 얼려먹기 (Stack과 Deque 둘다 구현해보기)


입출력 조건
입력 예제

DFS/BFS 모두가 가능한 문제

이 문제는 내가 편한 방법의 알고리즘을 사용해서 하면된다.

  1. DFS 사용이 가능하다는 건 : Stack 방식 (재귀함수)
    1. 정의대로 꾸역꾸역 탐색 기본으로 구현을 하는 방식
    2. 기능 중심으로 재귀함수로 구현도 가능하다.
  2. BFS 사용이 가능하다는 건 : Queue 방식
    1. 거리, 순서 중요할 수 있을 때
    2. 출발점에서 뭔가 점진적으로 진행할 때
    3. 최단거리 문제로 일반화 : 다익스트라 알고리즘
    4. 간단한 경우는 BFS로도 최단 거리가 해결이 된다.
    5. Deque 속도적인 부분에서 체크하자
    6. 주의) 절대로 재귀함수로 구현하면 안된다.

참고) 위의 DFS에 대한 문제를 BFS로 할 때..

1. 맵의 위치를 하나씩 받아 BFS를 돌린다.

2-1. BFS에서는 그 곳이 빈 얼음틀일 경우 현재 위치를 방문 처리 및 큐에 삽입/삭제한 뒤, 상하좌우의 위치를 확인하고 해당 위치가 빈 얼음틀일 경우 큐에 해당 위치를 삽입한다.

2-2. BFS에서는 그 곳이 빈 얼음틀이 아닐 경우 0을 반환한다.

3. 큐가 비었을 때 1을 반환하고 BFS를 종료한다.

4. 1 ~ 3번을 n x m 번만큼 반복하고 BFS가 하나씩 돌아갈 때마다 값을 하나씩 증가시켜준다.

#### 얼음 생성 문제 ###

# 고민 1) 왜 이문제가 탐색인가? 
#       ==> 카운팅에 대한 조건
#           0으로 시작할 수 있으면,, 0으로 연결된 거 다 찾아서
#           1로 바꿔줘 !!! LRUD
#           ==> 주어진 환경에 맞게 니가 다 찾아줘라!

# 고민 2) 탐색이라고 하면 여러 탐색 알고리즘 중에서 .. DFS? BFS? 다른거? 선택!!
# 찾아가는 과정이 순차적일 필요도 없고 그냥 다 찾아내는게 중요하다보니
# DFS, BFS 둘다 상관 없다. 속도 제외하고는
#       ==> BFS는 어제 해봤으니 직접 해보시면 되고
#       ==> DFS로 재귀함수

# 고민 3) 이 문제의 해결하는 큰 틀이 뭐지?
#       ===> 카운팅 조건 특정 조건이 존재할 때 카운팅해야 한다.
#       카운팅에 대한 조건?? : 0으로 상하좌우로 연결된 덩어리들이 될 때 마다!
#       (0으로 이어진 덩어리가 몇 개인지 찾는거)
#       --> 조건 : 그 지점에서 일단 얼음을 얼릴 수 있다면 칸막이까지 얼릴 수 있을 때까지 진행 후
#       다 얼리면 count를 +1
# start 출발점을 전부 다 출발 해봐야 한다. 

N, M = map(int, input().split(" ")) #N, M 행렬 크기 입력 받기

graph = int(input()) # graph의 형태를 입력 받기, 얼음이 어떻게 생겼는지 받아야 함

# 시도1) BFS로 한다면... 대략적인 흐름으로 체크
# 큰 틀 : 모든 좌표에서 시작한다고 가정!!

#               for i in range(M):
#                    for j in range(N):
#                   ==> 2D의 (i,j)위치 : 탐색의 시작점
#                   if(i,j)로 시작할 수 있고, 다 탐색 끝나면:
#                          카운팅 +1

방법 1) BFS로 탐색하며 얼음 수를 카운팅하기

from collections import deque
def ice_bfs(row_init,col_init):

#1) 주어진 시작점 좌표가 시작할 수 있는지 없는지 체크
    if graph[row_init][col_init] == 0:
        graph[row_init][col_init] = 1
        q = deque([[row_init, col_init]])
        # 2) 방문할 곳들에 대한 할일에 대한 초기 세팅 
        # 참고) 한 일 graph에 직접 0을 1로 바꿔가면서 진행

        while True: #무한 루프 ... 빠져나갈 세팅!
            # 종료 조건: q의 값이 빌 때까지 break
            if not q:
                return 1 # 시작은 가능하고, 할 것 다하고,, 최종 결과 1
            # ==> 할 일에 대한 본격 세팅
            # 갈 곳에 대한 선택
            row, col = q.popleft() # bfs로 돌리기 위해서 왼쪽에서 선택, dfs면 q.pop()
            for dx, dy in [ [0,-1], [0,1], [1,0], [-1,0] ]:
                # 이동 가능한지 체크 in/out 체크

                if 0 <= row+dx < n and 0 <= col+dy < m:
                    # ---> 얼릴 수 있는지 체크 : 1/0
                    if graph[row+dx][col+dy] == 0:
                        graph[row+dx][col+dy] = 1
                        q.append([row+dx,col+dy])

    else:
        # 주어진 좌표에서 시작조차 못하는 경우
        return 0
        
        
        
#test 할 떄 입력조건을 다음과 같이 보았을 때

graph_45 = [
    [0,0,1,1,0],
    [0,0,0,1,1],
    [1,1,1,1,1],
    [0,0,0,0,0]
]
n = 4
m =5

graph = graph_45

result = 0 #카운팅 변수
for row in range(n):
    for col in range(m):

        #시작점 후보(n,m)
        # if 시작할 수 있고 탐색이 끝나면..
        # +1 카운팅 갱신
        #ice_bfs(row,col)
        result += ice_bfs(row,col)

print(result) # 답은 3이 나온다. 얼음이 3개

방법 2) DFS의 재귀함수를 이용

def dfs_recursive(row, col):
    #기능 중심
    #기능 1) 주어진 점이 인바운드, 아웃바운드 체크
    if row <= -1 or row >= n or col <= -1 or col >= m: #out
        return False
    
    else: 
    #기능 2) 인바운드이면.... 얼음을 얼릴 수 있는지 체크!!!
    
        if graph[row][col] == 0:
            graph[row][col] = 1
            # 이 부분에 대해서 탐색의 방향성이나 우선순위가 있다면
            # 이 부분에 대해서 수정을 할 수 있다.
            # 아래 코드 : L 먼저 탐색하고, 진행 됨. 오른쪽부터 탐색해야한다면 R이 위로 가야 함.
            dfs_recursive(row,col-1) #L : 그냥 제가 L을 먼저 했기 때문에.. 막히기 전까지 왼쪽으로 쭉 가게 된다.
            dfs_recursive(row,col+1) #R
            dfs_recursive(row-1,0) #U
            dfs_recursive(row+1,0) #D
            return True
        return False
  
#test용 코드 
graph_45 = [
    [0,0,1,1,0],
    [0,0,0,1,1],
    [1,1,1,1,1],
    [0,0,0,0,0]
]
n = 4
m =5
graph = graph_45

result = 0
for row in range(n):
    for col in range(m):
        if dfs_recursive(row,col) == True: # 재귀함수의 끝에는 True를 
																		       # 반환하기 떄문에 비교 가능
            result +=1

print(result)

참고 : 여러 유형 중 dfs, bfs중 하나

1. 양과 늑대

https://school.programmers.co.kr/learn/courses/30/lessons/92343\

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

2. 양궁대회

https://school.programmers.co.kr/learn/courses/30/lessons/92342

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

 

3. DB/DBMS 그리고 Mysql


데이터베이스란?

  • 데이터의 집합
  • 여러 명의 사용자나 응용프로그램이 공유하는 데이터들
  • 동시에 접근 가능해야
  • 데이터의 저장 공간 자체

DBMS

  • 데이터베이스 매니지먼트 시스템
  • 데이터베이스를 관리, 운영하는 역할

DB/DBMS의 특징

  1. 데이터의 무결성
    1. 데이터베이스 안의 데이터는 오류가 없어야 제약 조건이라는 특성을 가진다.
  2. 데이터의 독립성
    1. 데이터베이스 크기 변경하거나 데이터 파일 저장소 변경시 기존에 작성된 응용프로그램은 전혀 영향을 받지 않아야 함
  3. 보안
    1. 데이터베이스 안의 데이터에 데이터를 소유한 사람 or 접근 허가 사람만 접근할 수 있어야 한다.
    2. 접근할 때도 사용자의 계정에 따라 다른 권한이 주어짐
  4. 데이터 중복의 최소화
    1. 동일한 데이터가 중복 저장되지 않도록 함
  5. 응용프로그램 제작 및 수정이 쉽다.
    1. 통일된 방식으로 응용프로그램 작성 가능
    2. 유지보수 용이
  6. 데이터의 안전성이 향상 된다.
    1. 대부분의 DBMS에서 제공하는 백업 기능을 이용할 수 있다.
    2. 데이터가 깨지는 경우 원상으로 복원, 복구하는 방법이 명확해짐

+) 참고 Mac용 Mysql 설치


우선, mac을 사용하는 경우 윈도우의 설치 방법과는 차이가 있으므로 주의 해야 한다.

설치부터 mac용 path 설정은 이 분의 블로그에 자세히 작성되어 있어서 참고하면 좋을 것 같다!

https://signature95.tistory.com/27

 

Mac OS mysql path 설정

mysql을 공부하려고 먼저 mysql을 설치하였는데, 이게 맥의 터미널에서는 path설정이 매우 복잡해서 여러 사이트를 찾아보았다. 참고한 사이트 https://devdotcode.com/how-to-add-mysql-to-the-path-in-mac-os/ How to a

signature95.tistory.com

SQL 예제와 관련된 내용은 내일부터 SQL 카테고리에 올려야겠다.