데이터 부트캠프 - Today I Learned

[스파르타 내일배움캠프 / 데이터 분석 트랙] TIL(Today I Learned)_4주차_24.12.16

onion95 2024. 12. 16. 21:39

Today's Goals

1. SQL 꾸준한 연습

2. 파이썬 익숙해지기 - 파이썬 코드카타, pandas 기초 다지기, 파이썬 개인과제

 

 

오늘 추가 발제가 있다는걸 까맣게 잊었다.

드디어 본격적인 파이썬 분석의 지옥으로...

 

pandas 항상 import 해서 쓰긴 했지만 아직도 뭐가 뭔지 모른다.

그놈의 df는 왜 계속 점을 찍고 다른 함수들이 연달아 나온건지..

 

이번엔 이녀석을 좀 이해하면 소원이 없겠다.

 

 

1. SQL 연습

 

SQL 코드카타


 

[CROSS JOIN, 그리고 잊고있던 COUNT(*)의 주의점]

select a.student_id, a.student_name, b.subject_name, count(c.subject_name) as attended_exams
from Students a
cross join Subjects b
left join Examinations c
on a.student_id = c.student_id and b.subject_name=c.subject_name
group by a.student_id, b.subject_name
order by student_id, subject_name
  • 학생별 과목별 시험 응시 횟수를 구하는 문제였음
  • 테이블은 학생정보, 과목이름, 학생 id와 응시 과목 이렇게 3가지
  • 도대체 과목 이름만 담긴 테이블이 왜 필요한지 의문 덩어리
  • 최종적으로, 응시 횟수가 0번인 것도 계산되어야 했고, 그렇다면 학생별로 세과목 모든 경우를 확인 할 수 있도록, 학생정보와 과목이름 간의 CROSS JOIN이 필요했기 때문이다!!!!!!!!!!!

 

  • 매일 inner 와 left만 써서 잊고 있던 cross join의 개념을 확인하고 가자

  • 두 테이블의 행x행 만큼 행이 생성되고, on을 사용할 필요가 없음
  • 따라서 위 쿼리에서 세 테이블을 join 하고 나면 나오는 결과는 아래와 같음

  • 여기서 응시 횟수를 count 할 때, count(*)를 하면 안되는 이유, *는 null인 경우까지 모두 세어주는 녀석인걸 잊지 말아야함

 

 

 

 

2. 파이썬 자신감 회복기

 

Python 전처리 강의 - Pandas


[Pandas의 자료형, 시리즈와 데이터프레임]

  • series는 컬럼 하나, series가 여러개 모여서 dataframe이 되는 것
  • 각 컬럼의 값을 줄 세워서, 행별로 그 순서를 정해준 것이 index > 인덱스는 기본적으로 순서대로 숫자가 부여되지만 따로 값을 지정할 수도 있음

 

 

[csv 저장과 index]

  • csv 저장시, index 설정에 따라 파일을 불러올 때, 인덱스가 하나의 컬럼이 되어있을 수 있음
df.to_csv('./newfile.csv', index = False)
  • index = False : 인덱스를 하나의 컬럼으로 저장하지 않음(인덱스 없는 테이블)
  • index = True는 기본값, 이 상태로 저장했다면 불러올 때, read_csv(경로, index_col=0) 를 써준다면 인덱스 없이 불러와짐 

 

 

[float 타입은 int로 변경할 수 있다]

  • string에서 int로는 변경할 수 없음(소수점 아래 값이 어디고, 정수가 어딘지 문자열에서는 구분할 수 없음)
  • float에서 int로 변경 시, 소수점 아래는 버림되고 기존 값에서 정수 부분만 가져오는 것

 

 

[데이터프레임에서 특정 영역을 확인하는 방법, loc & iloc]

  • iloc[인덱스값, 컬럼값] : 행번호 열번호로 데이터 선택
  • loc[인덱스값, 컬럼값] : 행이름 열이름으로 데이터 선택
  • iloc[a:b:c] : a는 시작행 위치, b는 마지막행 위치+1, c는 불러올 간격
  • iloc[ : , 컬럼순서] : 행이 아닌 열(컬럼)을 가져오는 것

 

 

[데이터프레임에서 컬럼을 불러오는 법 - loc을 쓰지 않아도 된다.] 

  • df['컬럼1'] : 컬럼1의 값을 확인
  • df['컬럼1', '컬럼2'] 는 오.류 > df[['컬럼1', '컬럼2']] 처럼 두번을 묶어줘야 함 > []안에 쓰여진 컬럼 순서대로 데이터프레임도 출력됨

 

 

[불리언 인덱싱이란]

  • 데이터프레임 안에 조건을 넣어 원하는 조건에 해당하는 경우만 필터링하여 데이터프레임으로 확인하는 것
  • df[df['gender'] == 'Male'] : gender 컬럼에서 Male에 해당하는 행만 필터링하여 데이터프레임을 출력함(모든 컬럼이 모두 나오고 gender 컬럼만 출력되지 않음)
  • 조건 여러개도 가능 > [(조건) & / | (조건)]

 

 

[df.loc도 불리언 인덱싱이 가능하다]

  • df.loc[조건, 컬럼1:컬럼3] : 조건에 해당하는 행을 모두 필터링하고, 컬럼1~컬럼3까지만 가져옴
  • 위에서 컬럼 범위를 설정하지 않으면 모든 컬럼이 출력됨

 

 

[isin()도 불리언 인덱싱]

  • df[df[컬럼명].isin([리스트])] : 리스트 안에 있는 값에 해당되는 행을 모두 출력함

 

 

[불리언 인덱싱의 가독성을 좋게 하는 방법]

  • 불리언 인덱싱의 원리는, 조건에 해당하는 df['gender'] == 'Male'를 출력하면 각 행마다 True, False 값이 반환되고, 이걸 다시 df로 묶어 True인 행만 반환되는 것
condition1 = data['컬럼명1'] < 80
condition2 = data['컬럼명2'] >= 50

condition = condition1 & condition2
data[conditon]
# 또는
df[condition1 & condition2]
  • 위와 같이 각 조건을 각각의 변수에 할당하고, 최종적으로 필터링해도 가능
  • 변수 하나에 여러 조건을 넣는 것도 가능한데, 이 때, 가독성 향상을 위해 조건 줄바꿈을 잘 활용해야 함
  • 파이써는 띄어쓰기, 줄바꿈에 민감하므로 연결되는 코드일 때, \를 꼭 넣어줘야함
condition = (data['컬럼명1'] < 80) \
            & (data['컬럼명2'] >= 50)\
            & (data['컬럼명3'] >= 10)
data[conditon]

 

 

[날짜형으로 변환하기]

  • df['컬럼'] = pd.to_datetime( df['컬럼'] )

 

 

[데이터 프레임을 합치고 싶다면 concat]

  • concat의 기본 활용은 SQL에서 union과 유사함(axis = 0 이 기본값이기 때문)
  • pd.concat([df1, df2], axis=1) : 데이터 프레임을 좌우로 합치는 것
  • 컬럼이 부족하거나, 행이 부족한 부분이 있다면 null로 채워짐

 

 

[join처럼 데이터 프레임을 합치고 싶다면 merge]

  • SQL의 join과 유사한 방법
  • pd.merge(df1, df2, on='기준컬럼', how = 'join 종류') : how의 기본값은 'inner'이고 outer, left, right 모두 가능

 

 

[그룹별 집계가 필요하다면 groupby()]

  • df.groupby('컬럼').집계함수()
  • df.groupby('컬럼').agg(list) : 컬럼의 그룹별 값을 리스트로 묶어 반환
  • 그룹화한 컬럼 자체를 집계하려는 것이 아니라면(평균함수를 쓸건데 그룹화한 컬럼은 문자열인 경우 등) 집계함수를 쓸 때 오류가 남
  • df.groupby('기준컬럼')['컬럼명'].집계함수()  : 특정 컬럼에 대한 집계값만 보고싶은 경우 
  • groupby()안에 컬럼은 여러개 들어갈 수 있음
  • 집계함수 또한 agg()하고 괄호 안에 여러개 가능('mean','sum' 이런식으로 넣으면 그룹별 평균과 합을 모두 구하는것)
  • df.groupby('기준컬럼').agg({'컬럼1' : ['max', 'sum'], '컬럼2' : ['sum'], .... }) : 기준컬럼에 따라 그룹화 하여, 불러오는 컬럼마다 다른 집계함수를 적용하고 싶을 때

 

 

[pivot 테이블 만드는 법]

  • pivot명 = df.pivot_table(index='행이름으로 쓸 컬럼' , columns = '컬럼명으로 쓸 컬럼' , values = '행-열에 해당하는 값으로 넣을 컬럼', aggfunc = 'sum or.... 집계종류')
  • columns에는 여러 컬럼이 리스트로 들어갈 수 있는데, 이는 그룹화와 비슷한 의미임 > 예를 들면 컬럼1에서 값이 a인 것 중 컬럼2에서 x인 경우와 y인 경우가 나누어 표시됨

 

 

[정렬하는 법, sort_values]

  • df.sort_values(by='컬럼명', ascending = False) : 컬럼을 기준으로 내림차순(False) 정렬, 오름차순(True) 정렬
  • df.sort_index(ascending = ) : 인덱스 값을 기준으로 정렬

 

 

 

Python 개인 과제 - 해답 풀이


[이메일 유효성 검사 문제, 끝 문자(접미사) 찾기 - endswith]

#문제4
email_list = [
    "example@example.com",  # 유효한 이메일
    "yangbujang@email.co.kr",  # 유효한 이메일
    "iamhungry.com",  # 틀린 이메일 -> @ 누락
    "@da.com",  # 틀린 이메일 -> ID 누락
    "daislove@isnit",  # 틀린 이메일 -> 도메인 잘못됨
]

def check_email(email_list) :
    for i in email_list :
        if ('@' in i) and ('.com' in i or '.co.kr' in i) and i.index('@') > 0 :
            print(f"유효한 이메일입니다: '{i}'")
        elif '@' not in i :
            print(f"유효하지 않은 이메일입니다: '{i}' 이유: '@가 누락'")
        elif i.index('@') == 0 :
            print(f"유효하지 않은 이메일입니다: '{i}' 이유: 'ID가 누락'")
        elif '.com' not in i and '.co.kr' not in i :
            print(f"유효하지 않은 이메일입니다: '{i}' 이유: '적절하지 않은 도메인'")
check_email(email_list)
  • 위 문제를 풀면서, 도메인 유효성을 검사할 때, .com이 메일 문자열 안에 있다 해도 그 뒤에 또 다른 문자가 붙으면 유효하지 않은 이메일이 된다는 의문이 있었음( .comabc 같은)
  • 풀이 강의에서 처음 들은 내장함수, endswith() 
  • not i.endswith('.com') and not  i.endswith('.co.kr')을 하면 com과 co.kr로 끝나지 않는 메일을 검사할 수 있음
  • 번외 : 시작 문자 찾기는 startswith()으로