728x90
반응형

데이터 처리 시각화 (1)

🍀 포항시 버스데이터 수집 가공 시각화 

  • URL : 국가교통 데이터 오픈마켓
  • 데이터 명 : 포항시 BIS 교통카드 사용내역

🍀 메타 정의서

 

샘플링

🍀 1 건 샘플링하기

  • 여러개의 파일 데이터를 통합하는 경우에는 한개 파일을 기준으로 사용할 컬럼을 정의하여 가공 후 반복 처리하면 편하다.

  • 0번 파일의 csv 데이터 읽어오기
    - 데이터프레임 이름 : df_bus_cart_org
file_path = "./01_data/org/trfcard(0)/trfcard.csv"
df_bus_card_org = pd.read_csv(file_path)
df_bus_card_org.head(1)

 

🍀 결측치가 있는지 정보확인하기

df_bus_card_org.info()

 

  • 행의 갯수 16185
  • 열의 갯수 13
  • age_type 결측치 존재

 

 

 

 

 

 

 

 

 

 

 

 

컬럼명 수정하기

🍀영문 컬럼명을 한글로 수정하기

  • 데이터프레임 이름 : df_bus_cart_col_org

🍀메타정의서의 영문명, 한글명 컬럼 읽어들이기

file_path = "./01_data/org/trfcard(0)/trfcard_columns.xlsx"
df_bus_card_col_org = pd.read_excel(file_path,
                                   header=2,
                                   usecols="B:C")
df_bus_card_col_org

 

🍀df_bus_cart_col_org의 데이터를 딕셔너리로 변환하기

  • 컬럼명의 이름을 매핑하여 변경하기 위해서는 컬럼정의 값을 key : value 딕셔너리 타입으로 정의해야 함
    - 예시 : {영문명 : 한글명, 영문명 : 한글명....}
  • Index로 값 조회해보기
    •  iloc[행, 열] index location : 인덱스 번호를 이용하는 방식
      - 특정 위치에 접근할 때 iloc를 많이 사용한다
    • loc[행값, 열값] : 눈에 보이는 인덱스 값을 이용하는 방식
print(df_bus_card_col_org.iloc[0, 0])
print(df_bus_card_col_org.iloc[0, 1])
print(df_bus_card_col_org.iloc[1, 0])
print(df_bus_card_col_org.iloc[1, 1])
print("-----------------------------")

print(df_bus_card_col_org.loc[0, "컬럼명 (영문)"])
print(df_bus_card_col_org.loc[0, "컬럼명 (한글)"])


  • 딕셔너리 변수명 : df_bus_card_col_new_dict
  • 영문명은 key로, 한글명은 value로 만들기
    - 예시  : {'on_date' : '승차시각', 'off_date' : '하차시간'}
  • 방법 1
df_bus_card_col_new_dict = dict(zip(df_bus_card_col_org["컬럼명 (영문)"],
                                    df_bus_card_col_org["컬럼명 (한글)"]))
print(df_bus_card_col_new_dict)​

 


  • 방법 2
df_bus_card_col_new_dict = {}
for k, v in zip(df_bus_card_col_org.iloc[:, 0], df_bus_card_col_org.iloc[:, 1]) :
    # print(k,v)
    df_bus_card_col_new_dict[k] = v
df_bus_card_col_new_dict​

 



🍀컬럼명 변경하기

  • 메모리에 반영되지 않고 보이는 것만 바꿈
    df_bus_card_org.rename(columns=df_bus_card_col_new_dict)​

 

↓원본을 조회해보면 컬럼은 바뀌지 않았다.


  • inplace = True로 변경사항을 메모리에 반영하기
    원본까지 컬럼이 변경 된다.
df_bus_card_org.rename(columns=df_bus_card_col_new_dict, inplace = True)

 

 

데이터 분석하기

🍀분석 주제

  • 대주제 : 포항시 버스 이용량 분석
  • 소주제
       <버스 이용량 분석>
       * 기준월 및 기준일자별 버스 이용량 분석 비교
       * 기준일 및 시간대별 버스 이용량 분석 비교
       * 기준시간 및 시간(분)별 버스 이용량 분석 비교

       <버스 내 체류시간 분석>
       * 기준월 및 기준일자별 버스 체류시간(하차시간 - 승차시간) 분석 비교
       * 기준일 및 시간대별 버스 체류시간 분석 비교
       * 기준시간 및 시간(분)별 버스 체류시간 분석 비교
       
       * 승하차정류장 구간별(정류장 체류시간의 합) 버스 내 체류시간
        - 체류시간(분) 상위 30건 분석 비교

🍀 분석을 위한 데이터 가공하기

  • 데이터 프레임 복제하기
df_bus_card_kor = df_bus_card_org.copy()

 

  • 승차시각과 하치시간 데이터 타입을 문자열로 변환하기
    - astype() : 데이터 형변환 함수. replace 지원안하기 때문에 바뀐 형상을 자기 자신에게 반영시켜야 한다.
df_bus_card_kor = df_bus_card_kor.astype({"승차시각" : "str",
                                          "하차시각" : "str"})
df_bus_card_kor.info()

 

승차시각과 하차시각의 Dtype이 object로 변환

 

 

 

 

 

 

 

 

 

 

 

 

 

 


  • 분석에 필요한 컬럼 추출하기
df_bus_card = df_bus_card_kor[["승차시각", "하차시각", "승객연령", "환승여부",
                               "추가운임여부", "승차정류장", "하차정류장"]].copy()
df_bus_card.head()


  • 승차시각과 하차시각의 데이터 타입을 날짜타입으로 변경하기
    - 문자만 날짜 타입으로 바꿀 수 있음
df_bus_card["승차시각"] = pd.to_datetime(df_bus_card_kor.loc[:, "승차시각"])
df_bus_card["하차시각"] = pd.to_datetime(df_bus_card_kor.loc[:, "하차시각"])
df_bus_card.info()

 

 

승차시각과 하차시각의 Dtype이 datetime으로 변환

 

 

 

 

 

 

 

 


🍀 가공한 데이터 새로운 컬럼으로 만들기

  • 버스 내 체류시간(분단위) 컬럼만들기
    - 컬럼명 : 버스내 체류시간(분)

(df_bus_card.iloc[0, 1] - df_bus_card.iloc[0, 0])


  • 위의 결과에서 total_seconds를 사용해 결과를 초단위로 변환하고 60을 나누어 분으로 나타난다.
round((df_bus_card.iloc[0, 1] - df_bus_card.iloc[0, 0]).total_seconds()/60,2)


  • 체류시간(분) 계산 및 컬럼 생성
    - .dt : 행 하나하나를 date type으로 바꿔서 실행
df_bus_card["버스내체류시간(분)"] = round((df_bus_card["하차시각"] - \
                                   df_bus_card["승차시각"]).dt.total_seconds()/60,2)
df_bus_card


  • 기준년도, 기준월, 기준일, 기준시간, 기준시간(분) 컬럼 생성하기
# - 기준년도
# .dt.year : 날짜 타입에서 연도만 추출
df_bus_card["기준년도"] = df_bus_card["승차시각"].dt.year

# - 기준월
df_bus_card["기준월"] = df_bus_card["승차시각"].dt.month

# - 기준일
df_bus_card["기준일"] = df_bus_card["승차시각"].dt.day

# - 기준시간
df_bus_card["기준시간"] = df_bus_card["승차시각"].dt.hour

# - 기준시간(분)
df_bus_card["기준시간(분)"] = df_bus_card["승차시각"].dt.minute
df_bus_card

728x90
반응형
728x90
반응형

DB 프로그램 실습

🍀 DB프로그램

  • X : 입력 값 바뀜
  • △ : 고정으로 둘 수도 있고, 안 둘 수도 있다
  • 나머지 : 무조건 고정 → class로 설정

    1. 데이터베이스 설정 정보 준비
    2. DB 접속하기 : 커넥션(connect)
    3. 커서 받아오기 : cursor()
    4. 구문 작성하기 : sql(조회, 입력, 수정, 삭제)  --- X
    5. 구문 실행하기 :  execute() -> 결괏값은 체크 --- △
    6. 조회인 경우 커서에서 데이터 추출하기 --- △
            → 한건 : fetchone()
            → 여러 건 : fetchall()
    7. 조회결과 데이터프레임 또는 웹페이지에 출력 --- △
        →  데이터프레임 : pd.DataFrame(여러 건), pd.DataFrame([한건])
    8. 조회가 아닌 경우 : 처리 결과 조건으로 확인하기 if "처리 성공" else "처리 실패" --- △
    9. DB정보 반환하기
            - 반환 순서 : cursor > connect

🍀 class 설정

  1. 클래스 생성
    - 클래스 이름 : PowerClass
  2. 클래스 내에 함수들 정의
  3. 외부에서 조회/입력/수정/삭제 처리하기

🍀 작성한 코드

class PowerClass:
    def __init__(self):
        self.conn = None
        self.host = "localhost"
        self.user = "gjuser"
        self.password = "dbdb"
        self.db = "gjdb"
        self.charset = "utf8"
        self.cursorclass = pymysql.cursors.DictCursor
        self.autocommit = True
        
    def connect(self):
        try:
            self.conn = pymysql.connect(
                host=self.host,
                user=self.user,
                password=self.password,
                db=self.db,
                charset=self.charset,
                autocommit=self.autocommit,
                cursorclass=self.cursorclass
            )
            print("DB접속 성공 >>> ", self.conn)
        except Exception as e:
            print("DB Server Checking...", e)

    
    def cursor(self):
        cur = self.conn.cursor()
        return cur

    def close(self):
        self.cur = self.conn.cursor()
        try:
            self.cur.close()
            self.conn.close()
            print("접속종료")
        except :
            print("이미 모든 커서와 접속 정보가 반납되었습니다.")
def read(sql, cur):
    if sql[:6] == "Select" :
        cur.execute(sql)
        rows = cur.fetchall()
        ymd_power_df = pd.DataFrame(rows)
        print(ymd_power_df)
    else:
        print('다시입력')

def insert(sql, cur):
    if sql[:6] == "Insert" :
        rs_cnt = cur.execute(sql)
        if rs_cnt > 0:
            print(f'{rs_cnt}건이 입력되었습니다.')
        else:
            print(f'입력되지 않았습니다')
    else:
        print('다시입력')

def update(sql, cur):
    if sql[:6] == "Update" :
        rs_cnt = cur.execute(sql)
        if rs_cnt > 0:
            print(f'{rs_cnt}건이 수정되었습니다.')
        else:
            print(f'수정되지 않았습니다')
    else:
        print('다시입력')
            

def delete(sql, cur):
    if sql[:6] == "Update" :
        rs_cnt = cur.execute(sql)
        if rs_cnt > 0:
            print(f'{rs_cnt}건이 삭제되었습니다.')
        else:
            print(f'삭제되지 않았습니다')
    else :
        print('다시입력')
def main():
    db = PowerClass()
    db.connect()
    cur = db.cursor()
     
    
    while True:
        choice = input("""
                        < 조회/입력/수정/삭제 하기>
                        1. 조회
                        2. 입력
                        3. 수정
                        4. 삭제
                        5. 접속종료
                        원하는 번호(1~4)를 선택하세요
                        """)
        

        if choice == "1":
            sql = input("""
                        sql 작성하기
                    """)
            read(sql, cur)
               
        elif choice == "2":
            sql = input("""
                        sql 작성하기
                    """)
            insert(sql, cur)
                                                                                              
        elif choice == "3":
            sql = input("""
                        sql 작성하기
                    """)
            update(sql, cur)
        
        elif choice == "4":
            sql = input("""
                        sql 작성하기
                    """)
            delete(sql, cur)

        elif choice == "5":
            db.close()
            break
               
        else:
            print("다시 선택해주세요")

if __name__ == "__main__":
    main()
DB접속 성공 >>>  <pymysql.connections.Connection object at 0x000001E355B99150>

                        < 조회/입력/수정/삭제 하기>
                        1. 조회
                        2. 입력
                        3. 수정
                        4. 삭제
                        5. 접속종료
                        원하는 번호(1~4)를 선택하세요
                         2

                        sql 작성하기
                     Insert Into time_power_demand(         ymd, time, power     ) Values (         '2020-12-31', '24시', 1234     )
1건이 입력되었습니다.

                        < 조회/입력/수정/삭제 하기>
                        1. 조회
                        2. 입력
                        3. 수정
                        4. 삭제
                        5. 접속종료
                        원하는 번호(1~4)를 선택하세요
                         1

                        sql 작성하기
                     Select * From time_power_demand         Where ymd = '2020-12-31'
          ymd time   power
0  2020-12-31  24시  1234.0

                        < 조회/입력/수정/삭제 하기>
                        1. 조회
                        2. 입력
                        3. 수정
                        4. 삭제
                        5. 접속종료
                        원하는 번호(1~4)를 선택하세요
                         3

                        sql 작성하기
                     Update time_power_demand set power = 5679         Where ymd = '2020-12-31'             And time = '24시'
1건이 수정되었습니다.

                        < 조회/입력/수정/삭제 하기>
                        1. 조회
                        2. 입력
                        3. 수정
                        4. 삭제
                        5. 접속종료
                        원하는 번호(1~4)를 선택하세요
                         1

                        sql 작성하기
                     Select * From time_power_demand         Where ymd = '2020-12-31'
          ymd time   power
0  2020-12-31  24시  5679.0

                        < 조회/입력/수정/삭제 하기>
                        1. 조회
                        2. 입력
                        3. 수정
                        4. 삭제
                        5. 접속종료
                        원하는 번호(1~4)를 선택하세요
                         4

                        sql 작성하기
                     Delete         From time_power_demand         Where ymd = '2020-12-31'             And time = '24시'
1건이 삭제되었습니다.

                        < 조회/입력/수정/삭제 하기>
                        1. 조회
                        2. 입력
                        3. 수정
                        4. 삭제
                        5. 접속종료
                        원하는 번호(1~4)를 선택하세요
                         1

                        sql 작성하기
                     Select * From time_power_demand         Where ymd = '2020-12-31'
Empty DataFrame
Columns: []
Index: []

                        < 조회/입력/수정/삭제 하기>
                        1. 조회
                        2. 입력
                        3. 수정
                        4. 삭제
                        5. 접속종료
                        원하는 번호(1~4)를 선택하세요
                         5
접속종료
728x90
반응형
728x90
반응형

 

DB 프로그램 순서


1. 데이터베이스 설정 정보 준비
2. DB 접속하기 : 커넥션(connect)
3. 커서 받아오기 : cursor()
4. 구문 작성하기 : sql(조회, 입력, 수정, 삭제)
5. 구문 실행하기 :  execute() -> 결과값은 체크
6. 조회인 경우 커서에서 데이터 추출하기
        -> 한건 : fetchone()
        -> 여러건 : fetchall()
7. 조회결과 데이터프레임 또는 웹페이지에 출력
    -> 데이터프레임 : pd.DataFrame(여러건), pd.DataFrame([한건])
8. 조회가 아닌 경우 : 처리 결과 조건으로 확인하기 if "처리 성공" else "처리 실패"
9. DB정보 반환하기
        - 반환 순서 : cursor > connect
    

데이터 조회하기

🍀 데이터베이스 연결

import pymysql
### 접속 정보
# - 접속 허가를 위한 정보
# - 접속 id or 도메인
host = "localhost"
# - 사용자계정
user = "gjuser"
# - 패스워드
password = "dbdb"
# - 데이터베이스명
db = "gjdb"
# - 한글처리
charset = "utf8"
# - 조회시 컬럼명을 동시에 보여줄지 여부 설정
cursorclass = pymysql.cursors.DictCursor

 

🍀 DB 접속하기

  • connect로 서버에 연결 허가를 요청하여 conn에 허가를 받아 옴
try :
    conn = pymysql.connect(host = host,
                           user = user,
                           password = password,
                           db = db,
                           charset = charset,
                           # autocommit = autocommit,
                           cursorclass = cursorclass)
    print("DB접속 성공 >>> ", conn)
except :
    print("DB Server Checking...")

 

🍀 커서 받아오기

cur = conn.cursor()
cur

 

 🍀 Select 문 생성 후 DB에게 요청 및 응답 받기

  • execute가 "Select * from time_power_demand"라는 요청문을 서버에게 가지고 가서 그에 대한 답을 가져온다.
sql = "Select * from time_power_demand"
rs_cnt = cur.execute(sql)
print(f"{rs_cnt}건이 조회 되었습니다.")


🍀 조회결과가 여러 건인 경우

  • non 맵핑 방식
  • df.to_sql은 orm( object relation mapping, 객체 맵핑 방식)
  • cursorclass설정 시 딕셔너리 형태로 컬럼명과 같이 가져옴
  • cursorclass설정 안하면 튜플로 가져와서 컬럼명 없이 값만 나옴
    - [(), ()...] 
rows = cur.fetchall()
rows

 

🍀 조회결과 데이터프레임으로 출력

  • cursorclass설정을 안해서 컬럼명 없이 받아온 경우
    - ymd_power_df = pd.DataFrame(rows, columns=["ymd", "time", "power"]) 처럼 컬럼명 지정해서 코드 작성
ymd_power_df = pd.DataFrame(rows)
ymd_power_df

🍀 조회결과가 한 건인 경우

  • 한건 조회를 위한 sql 구문 생성하기
  • sql 구문 쓸 때 컴마 뒤에 띄어쓰기 꼭 하기
sql = """
        Select * From time_power_demand
        Where ymd = '2021-01-01'
            And time = '1시'
"""
sql
  • execute로 sql 구문을 DB서버에 요청하고, 결과 받아오기 
cur.execute(sql)

 

한 건의 결과가 나왔다는 의미

 

  • 받아오고 데이터 추출하기
row = cur.fetchone()
row

  • 데이터프레임에 담아서 보여주기
    - 데이터가 한 건만 추출 될 때는 딕셔너리 타입으로 추출되기 때문에 데이터프레임에 데이터를 담을 때는 리스트 타입으로 넣어야함
ymd_power_df = pd.DataFrame([row])
ymd_power_df

 

데이터 입력하기

🍀 입력을 위한 구문 생성하기

sql = """
    Insert Into time_power_demand(
        ymd, time, power
    ) Values (
        '2020-12-31', '24시', 1234
    )
"""
sql

 

🍀 구문 처리하고 결과 값 받아오기

  • 저장 요청 시에는 결과값이 숫자값으로 반환된다
    - 몇 건이 수행 됐는지 숫자값으로 나옴
rs_cnt = cur.execute(sql)
rs_cnt
print(f'{rs_cnt}건이 처리되었습니다')

 

🍀 처리가 잘 되었는지 확인하기

  • 0보다 크면 '1건이 입력되었습니다.' 출력
  • 0이하인 경우에는  "입력되지 않았습니다." 출력 
if rs_cnt > 0 :
    print(f'{rs_cnt}건이 입력되었습니다.')
else :
    print(f'입력되지 않았습니다')

 

데이터 수정하기

### ymd가 2020-12-31이고, 시간이 24시 인 행을 찾아서 power의 값을 5678로 수정하기

sql = """
        Update time_power_demand set power = 5679
        Where ymd = '2020-12-31'
            And time = '24시'    
"""
rs_cnt = cur.execute(sql)
if rs_cnt > 0 :
    print(f'{rs_cnt}건이 수정되었습니다.')
else :
    print(f'수정되지 않았습니다')

 

데이터 삭제하기

### ymd가 2020-12-31이고, 시간이 24시 인 행을 찾아서 삭제하기

sql = """
        Delete
        From time_power_demand
        Where ymd = '2020-12-31'
            And time = '24시'    
"""
rs_cnt = cur.execute(sql)
if rs_cnt > 0 :
    print(f'{rs_cnt}건이 삭제되었습니다.')
else :
    print(f'삭제되지 않았습니다')

데이터 조회 문제

🍀조회해보기

  • 한번도 구매한적이 없는 회원정보를 조회하기
  • 조회 컬럼 : 회원아이디, 회원이름
sql = """
    SELECT mem_id, mem_name
    FROM member
    WHERE not exists (SELECT cart_member FROM cart
WHERE cart_member = mem_id)
"""
rs_cnt = cur.execute(sql)
if rs_cnt > 0 :
    print(f'{rs_cnt}건이 조회되었습니다.')
else :
    print(f'조회되지 않았습니다')

  • 커서가 가지고 있는 결과 추출하여 데이터프레임 형식으로 나타내기
rows = cur.fetchall()
noMember = pd.DataFrame(rows)
noMember

 

종료하기

🍀커서 및 커넥션 정보 반납(종료)하기

try:
    cur.close()
    conn.close()
except :
    print("이미 모든 커서와 접속 정보가 반납되었습니다.")
728x90
반응형
728x90
반응형

📝문제 1 - 평균 일일 대여 요금 구하기

더보기

1. 문제 설명

다음은 어느 자동차 대여 회사에서 대여중인 자동차들의 정보를 담은 CAR_RENTAL_COMPANY_CAR 테이블입니다. CAR_RENTAL_COMPANY_CAR 테이블은 아래와 같은 구조로 되어있으며 CAR_ID, CAR_TYPE, DAILY_FEE, OPTIONS는 각각 자동차 ID, 자동차 종류, 일일 대여 요금(원), 자동차 옵션 리스트를 나타냅니다.

Column name  Type  Nullable
CAR_ID INTEGER FALSE
CAR_TYPE VARCHAR(255) FALSE
DAILY_FEE INTEGER FALSE
OPTIONS VARCHAR(255) FALSE

자동차 종류는 '세단', 'SUV', '승합차', '트럭', '리무진' 이 있습니다. 자동차 옵션 리스트는 콤마(',')로 구분된 키워드 리스트(예: '열선시트', '스마트키', '주차감지센서')로 되어있으며, 키워드 종류는 '주차감지센서', '스마트키', '네비게이션', '통풍시트', '열선시트', '후방카메라', '가죽시트' 가 있습니다.


2. 문제

CAR_RENTAL_COMPANY_CAR 테이블에서 자동차 종류가 'SUV'인 자동차들의 평균 일일 대여 요금을 출력하는 SQL문을 작성해주세요. 이때 평균 일일 대여 요금은 소수 첫 번째 자리에서 반올림하고, 컬럼명은 AVERAGE_FEE 로 지정해주세요.


3. 예시

예를 들어 CAR_RENTAL_COMPANY_CAR 테이블이 다음과 같다면

CAR_ID CAR_TYPE DAILY_FEE OPTIONS
1 세단 16000 가죽시트,열선시트,후방카메라
2 SUV 14000 스마트키,네비게이션,열선시트
3 SUV 22000 주차감지센서,후방카메라,가죽시트

 

'SUV' 에 해당하는 자동차들의 평균 일일 대여 요금은 18,000 원 이므로, 다음과 같은 결과가 나와야 합니다.

AVERAGE_FEE
18000

✏️ 작성한 코드

SELECT ROUND(AVG(DAILY_FEE)) as AVERAGE_FEE
from CAR_RENTAL_COMPANY_CAR
where CAR_TYPE = 'SUV'
GROUP BY CAR_TYPE;

 

📝문제 2 - 조건에 맞는 도서 리스트 출력하기

더보기

1. 문제 설명

다음은 어느 한 서점에서 판매중인 도서들의 도서 정보(BOOK) 테이블입니다. BOOK 테이블은 각 도서의 정보를 담은 테이블로 아래와 같은 구조로 되어있습니다.

Column name Type Nullable Description
BOOK_ID INTEGER FALSE 도서 ID
CATEGORY VARCHAR(N) FALSE 카테고리 (경제, 인문, 소설, 생활, 기술)
AUTHOR_ID INTEGER FALSE 저자 ID
PRICE INTEGER FALSE 판매가 (원)
PUBLISHED_DATE DATE FALSE 출판일

2. 문제

BOOK 테이블에서 2021년에 출판된 '인문' 카테고리에 속하는 도서 리스트를 찾아서 도서 ID(BOOK_ID), 출판일 (PUBLISHED_DATE)을 출력하는 SQL문을 작성해주세요.결과는 출판일을 기준으로 오름차순 정렬해주세요.


3. 예시

예를 들어 BOOK 테이블이 다음과 같다면

BOOK_ID CATEGORY AUTHOR_ID PRICE PUBLISHED_DATE
1 인문 1 10000 2020-01-01
2 경제 2 9000 2021-02-05
3 인문 2 11000 2021-04-11
4 인문 3 10000 2021-03-15
5 생활 1 12000 2021-01-10

 

조건에 속하는 도서는 도서 ID 가 3, 4인 도서이므로 다음과 같습니다.

BOOK_ID PUBLISHED_DATE
3 2021-04-11
4 2021-03-15

 

그리고 출판일 오름차순으로 정렬하여야 하므로 다음과 같은 결과가 나와야 합니다.

BOOK_ID PUBLISHED_DATE
4 2021-03-15
3 2021-04-11

4. 주의사항

PUBLISHED_DATE의 데이트 포맷이 예시와 동일해야 정답처리 됩니다.

✏️ 작성한 코드

SELECT BOOK_ID
	, DATE_FORMAT(PUBLISHED_DATE, '%Y-%m-%d') as PUBLISHED_DATE
from BOOK
where PUBLISHED_DATE like '2021%'
    AND CATEGORY = '인문'
order by PUBLISHED_DATE;

 

728x90
반응형
728x90
반응형

📝 문제

더보기

1. 문제 설명

코딩테스트를 준비하는 머쓱이는 프로그래머스에서 문제를 풀고 나중에 다시 코드를 보면서 공부하려고 작성한 코드를 컴퓨터 바탕화면에 아무 위치에나 저장해 둡니다. 저장한 코드가 많아지면서 머쓱이는 본인의 컴퓨터 바탕화면이 너무 지저분하다고 생각했습니다. 프로그래머스에서 작성했던 코드는 그 문제에 가서 다시 볼 수 있기 때문에 저장해 둔 파일들을 전부 삭제하기로 했습니다.

컴퓨터 바탕화면은 각 칸이 정사각형인 격자판입니다. 이때 컴퓨터 바탕화면의 상태를 나타낸 문자열 배열 wallpaper가 주어집니다. 파일들은 바탕화면의 격자칸에 위치하고 바탕화면의 격자점들은 바탕화면의 가장 왼쪽 위를 (0, 0)으로 시작해 (세로 좌표, 가로 좌표)로 표현합니다. 빈칸은 ".", 파일이 있는 칸은 "#"의 값을 가집니다. 드래그를 하면 파일들을 선택할 수 있고, 선택된 파일들을 삭제할 수 있습니다. 머쓱이는 최소한의 이동거리를 갖는 한 번의 드래그로 모든 파일을 선택해서 한 번에 지우려고 하며 드래그로 파일들을 선택하는 방법은 다음과 같습니다.

  • 드래그는 바탕화면의 격자점 S(lux, luy)를 마우스 왼쪽 버튼으로 클릭한 상태로 격자점 E(rdxrdy)로 이동한 뒤 마우스 왼쪽 버튼을 떼는 행동입니다. 이때, "점 S에서 점 E로 드래그한다"고 표현하고 점 S와 점 E를 각각 드래그의 시작점, 끝점이라고 표현합니다.
  • 점 S(luxluy)에서 점 E(rdxrdy)로 드래그를 할 때, "드래그 한 거리"는 |rdx lux| + |rdy luy|로 정의합니다.
  • 점 S에서 점 E로 드래그를 하면 바탕화면에서 두 격자점을 각각 왼쪽 위, 오른쪽 아래로 하는 직사각형 내부에 있는 모든 파일이 선택됩니다.

예를 들어 wallpaper = [".#...", "..#..", "...#."]인 바탕화면을 그림으로 나타내면 다음과 같습니다

이러한 바탕화면에서 다음 그림과 같이 S(0, 1)에서 E(3, 4)로 드래그하면 세 개의 파일이 모두 선택되므로 드래그 한 거리 (3 - 0) + (4 - 1) = 6을 최솟값으로 모든 파일을 선택 가능합니다.

(0, 0)에서 (3, 5)로 드래그해도 모든 파일을 선택할 수 있지만 이때 드래그 한 거리는 (3 - 0) + (5 - 0) = 8이고 이전의 방법보다 거리가 늘어납니다.

 

머쓱이의 컴퓨터 바탕화면의 상태를 나타내는 문자열 배열 wallpaper가 매개변수로 주어질 때 바탕화면의 파일들을 한 번에 삭제하기 위해 최소한의 이동거리를 갖는 드래그의 시작점과 끝점을 담은 정수 배열을 return하는 solution 함수를 작성해 주세요. 드래그의 시작점이 (lux, luy), 끝점이 (rdx, rdy)라면 정수 배열 [lux, luy, rdx, rdy]를 return하면 됩니다.


2. 제한사항

  • 1 ≤ wallpaper의 길이 ≤ 50
  • 1 ≤ wallpaper[i]의 길이 ≤ 50
    wallpaper의 모든 원소의 길이는 동일합니다.
  • wallpaper[i][j]는 바탕화면에서 i + 1행 j + 1열에 해당하는 칸의 상태를 나타냅니다.
  • wallpaper[i][j]는 "#" 또는 "."의 값만 가집니다.
  • 바탕화면에는 적어도 하나의 파일이 있습니다.
  • 드래그 시작점 (luxluy)와 끝점 (rdxrdy)는 lux rdxluy rdy를 만족해야 합니다.

3. 입출력 예

wallpaper result
[".#...", "..#..", "...#."] [0, 1, 3, 4]
["..........", ".....#....", "......##..", "...##.....", "....#....."] [1, 3, 5, 8]
[".##...##.", "#..#.#..#", "#...#...#", ".#.....#.", "..#...#..", "...#.#...", "....#...."] [0, 0, 7, 9]
["..", "#."] [1, 0, 2, 1]

 

4. 입출력 예

  • 입출력 예 #1
     문제 설명의 예시와 같은 예제입니다. (0, 1)에서 (3, 4)로 드래그 하면 모든 파일을 선택할 수 있고 드래그 한 거리는 6이었고, 6보다 적은 거리로 모든 파일을 선택하는 방법은 없습니다. 따라서 [0, 1, 3, 4]를 return합니다.

  • 입출력 예 #2
     예제 2번의 바탕화면은 다음과 같습니다.(1, 3)에서 (5, 8)로 드래그하면 모든 파일을 선택할 수 있고 이보다 적은 이동거리로 모든 파일을 선택하는 방법은 없습니다. 따라서 가장 적은 이동의 드래그로 모든 파일을 선택하는 방법인 [1, 3, 5, 8]을 return합니다.

 

  • 입출력 예 #3
     예제 3번의 바탕화면은 다음과 같습니다.모든 파일을 선택하기 위해선 바탕화면의 가장 왼쪽 위 (0, 0)에서 가장 오른쪽 아래 (7, 9)로 드래그 해야만 합니다. 따라서 [0, 0, 7, 9]를 return합니다.

 

  • 입출력 예 #4
     예제 4번의 바탕화면은 다음과 같이 2행 1열에만 아이콘이 있습니다.이를 드래그로 선택하기 위해서는 그 칸의 왼쪽 위 (1, 0)에서 오른쪽 아래 (2, 1)로 드래그 하면 됩니다. (1, 0)에서 (2, 2)로 드래그 해도 아이콘을 선택할 수 있지만 이전보다 이동거리가 늘어납니다. 따라서 [1, 0, 2, 1]을 return합니다.

✏️작성한 코드

코드 풀이를 해보자.

일단 X, Y를 행과 열로 생각한다.

  •  int[] X = new int[wallpaper.length]; 
    wallpaper 배열에 들어가 있는 값의 길이 만큼 X, Y도 출력되서 나오므로 X, Y를 wallpaper 만큼의 길이를 가진 리스트로 설정했다.
  • int xMin = Integer.MAX_VALUE;
    X, Y에 들어간 값 중 행의 최솟값과 행의 최댓값, 열의 최솟값과 열의 최댓값을 찾아야 하므로 각각을 변수로 설정한다.  Integer.MAX_VALUE는 JAVA에서의 정수 최댓값을 의미하기 때문에 최솟값을 구하려고 할 때 MAX_VALUE와 비교해야 한다.

  • for 문
    중첩된 루프를 사용하여 wallpaper 배열을 순회하면서 '#' 문자를 찾고, 해당 위치의 행과 열을 XY 배열에 저장하고 최솟값과 최댓값을 업데이트합니다.

  •  Math.min(A, X[i])
    Math.min(a, b)은 두 인자 값 중 작은 값을 리턴한다. 즉, A가 Integer.MAX_VALUE 이므로 X[i]이 그보다 더 클 수 없다. 그러므로 처음 들어오는 X[i]의 값이 A로 리턴되면서 새로 들어오는 X[i]과 비교하여 최소값을 반환하게 된다.

  • xMax = Math.max(C, X[i]+1);
    Max 값에 1을 더해준 이유는 파일이 교차되는 점에 위치해 있는게 아닌 한 개의 구역을 차지하고 있기 때문에 드래그 했을 때 마지막 위치가 구역의 시작이 아닌 마지막이므로 1을 더 해줘야 해야 한다.
class Solution {
    public int[] solution(String[] wallpaper) {
        int[] X = new int[wallpaper.length]; 
        int[] Y = new int[wallpaper.length];

        int xMin = Integer.MAX_VALUE;
        int yMin = Integer.MAX_VALUE;
        int xMax = Integer.MIN_VALUE;
        int yMax = Integer.MIN_VALUE;

        for (int i = 0; i < wallpaper.length; i++) {
            for (int j = 0; j < wallpaper[i].length(); j++) {
                if (wallpaper[i].charAt(j) == '#') {
                    X[i] = i;
                    Y[i] = j;

                    xMin = Math.min(A, X[i]);
                    yMin = Math.min(B, Y[i]);
                    xMax = Math.max(C, X[i]+1);
                   	yMax = Math.max(D, Y[i]+1);
                }
            }
        }

        int[] answer = {xMin, yMin, xMax, yMax};
        return answer;
    }
}

 
728x90
반응형
728x90
반응형

데이터 설계하기


🫐 테이블 설계하기

  • 테이블명, 컬럼명, 컬럼타입, null 여부에 대한 정의가 필요함
  • 산출물
    - 테이블 정의서
    - ERD
    - 스크립트 명세서

🫐 테이블 정의서

  • 자료형 길이 설정 시 한글은 *2
  • double
    소숫점을 가지는 많은 양의 데이터
    정밀도를 요구하는 데이터
    소숫점 아래 값이 많은 데이터

 

🫐 스크립트 명세서

  • SQL에 그대로 작성하여 테이블 생성

Create Table time_power_demand(
	ymd varchar(10) NOT NULL,
   time varchar(4) NOT NULL,
	power DOUBLE NOT null
);

 

DB 연결하기

🫐 데이터 저장시에 유용한 라이브러리

  • sqlalchemy
  • 설치 : pip install sqlalchemy

🫐  데이터 저장시에 유용한 라이브러리

  • pymysql
  • 설치 : conda install -c conda-forge pymysql
  • 설치 : pip install pymysql

🫐 Database 연결하기

from sqlalchemy import create_engine

### 연결정보 작성
db_connection_info = "mysql+pymysql://gjuser:dbdb@localhost:3306/gjdb"

### Database 연결하기
db_connection = create_engine(db_connection_info)
db_connection

데이터 프레임을 Table에 저장시키기

🫐 컬럼명 수정

  • 데이터프레임의 컬럼명을 테이블의 컬럼명과 같게 수정
  • 특정 컬럼명만 수정하고자 할 때
    → df.columns = [{"년도" : "ymd"}]
# - 데이터프레임의 컬럼명 전체수정하기
df.columns = ["ymd", "time", "power"]
df


🫐 데이터 저장하기

df.to_sql(name="time_power_demand",
         con=db_connection,
         index=False,
         if_exists="append")

 

8760 rows 저장 완료

 

 

 

SQL 조회시 값이 들어가 있는 것을 확인할 수 있다.

 

 

 

 

 

 

 

 

 

 

 


🫐 데이터베이스 자원 반환(접속 끊기)

  • 연결 종료하기
db_connection.dispose()

데이터 조회하기

🫐 데이터 조회

  • 조회시에는 pymysql 라이브러리 사용

🫐데이터베이스 연결

import pymysql
### 접속 정보
# - 접속 허가를 위한 정보
# - 접속 id or 도메인
host = "localhost"
# - 사용자계정
user = "gjuser"
# - 패스워드
password = "dbdb"
# - 데이터베이스명
db = "gjdb"
# - 한글처리
charset = "utf8"
# - 조회시 컬럼명을 동시에 보여줄지 여부 설정
cursorclass = pymysql.cursors.DictCursor

 

🫐 DB 접속하기

  • connect로 서버에 연결 허가를 요청하여 conn에 허가를 받음
try :
    conn = pymysql.connect(host = host,
                           user = user,
                           password = password,
                           db = db,
                           charset = charset,
                           # autocommit = autocommit,
                           cursorclass = cursorclass)
    print("DB접속 성공 >>> ", conn)
except :
    print("DB Server Checking...")

 

🫐 커서 받아오기

cur = conn.cursor()
cur

 

🫐 Select 문 생성 후 DB에게 요청 및 응답 받기

  • execute가 "Select * from time_power_demand"라는 요청문을 서버에게 가지고 가서 그에 대한 답을 가져 
sql = "Select * from time_power_demand"
rs_cnt = cur.execute(sql)
print(f"{rs_cnt}건이 조회 되었습니다.")

 

🫐 커서 및 커넥션 정보 반납(종료)하기

try:
    cur.close()
    conn.close()
except :
    print("이미 모든 커서와 접속 정보가 반납되었습니다.")
728x90
반응형

+ Recent posts