네이버 부스트캠프 AI Tech 5기를 합격하고, 첫 주를 무사히 보냈다. 한 주 동안 내가 배운 것 중 몰랐거나 중요하다고 생각한 부분 및 느낀 점을 써보려 한다.
- 파이썬은 플랫폼(OS)에 독립적인 인터프리터 언어이다.
컴파일러 언어 : 소스코드를 기계어로 먼저 번역하며 실행 속도가 빠르지만 많은 기억장소가 필요함
인터프리터 언어 : 별도 번역과정 없이 소스 코드를 실행 시점에 해석, 실행속도가 느리지만 메모리가 적게 필요
- 파이썬은 객체 지향 언어이며 동적 타이핑 언어이다.
동적 타이핑 언어 : 프로그램 실행 시점에 데이터 타입을 결정하는 특징을 가짐
- 기본적으로 변수는 메모리 주소를 가지고 있고 변수에 들어가는 값은 메모리 주소에 할당된다.
- 서로 다른 리스트 a, b에서 b = a를 실행 시 a와 b가 같은 메모리 주소를 가리키게 된다. 따라서 a 값을 변경하면 b 값도 변경된다.
- 이 때, b = a가 아닌 b = a[:] 로 하면 a 원소 값만 복사함 (copy)
- 2차원 리스트에선 b = a[:] 과 같은 copy를 해도 원소 값을 바꿀 수 없다. 이 때, deepcopy 메서드 이용해야 한다.
function
- is 연산은 memory 주소를 비교하고, == 연산은 값을 비교한다.
-5 ~ 256 사이 값은 같은 메모리 주소(정적 메모리)를 이용해 다른 변수에 할당해도 is 연산 시 True가 나온다.string
- 이진수 한 자리는 1bit로 저장되며, 1byte는 8bit로 28 = 256까지 저장 가능하다.
- r"string" 은 string 내부의 \n과 같은 기호들을 그대로 처리한다.
collections 라이브러리
- deque : 링크드 리스트의 특성을 지원해 줌
- extend, extendleft 함수도 지원함
- deque.rotate(1) : 오른쪽으로 1만큼 회전
- defaultdict : 기존 딕셔너리는 키 값이 없으면 KeyError가 나지만, defaultdict의 경우 키 값을 만들어 줌
- defaultdict(함수) 형태로 초기 밸류 값을 만들어 줄 수 있음
pythonic
- 람다 함수는 (lambda x, y: x + y)(50, 10) 과 같은 형태로 사용 가능
- iter([1, 2, 3])의 경우 1, 2와 그 다음 엘리먼트의 주소값을 같이 저장하게 됨 (3의 경우 다음 엘리먼트 존재하지 않으므로 엘리먼트만 저장)
- 이 때 iter 객체에 next 함수를 사용하면 다음 엘리먼트를 반환함
- generator를 사용하면 메모리를 아낄 수 있음
- generator는 iterator와 다르게 반복 객체의 모든 원소가 메모리에 할당되지 않고 호출된 원소만 할당됨
- []대신 ()를 사용해 생성
- yield 함수는 return과 다르게 호출돼도 함수를 종료하지 않는다. for문 또는 next를 이용해 호출될 때마다 한 개씩 yield 값 반환
- 가변인자 : asterisk(*)을 이용해 개수가 정해지지 않은 변수를 함수의 파라미터로 사용
- 가변인자는 함수 내에서 튜플로 사용함
- 키워드 가변인자: asterisk 두 개 (**)를 사용하면 dict type으로 함수 내에서 사용
- 함수에 전달할 땐 변수 이름과 값을 같이 전달 (e.g. f(a=1, b=2))
- 함수에선 파라미터로 키워드 형태로 넣고 그 뒤엔 값 형태로 넣으면 안 됨
- asterisk가 함수에 넘겨지는 가변인자에 포함되면 언패킹시킴
- e.g. f(1, *(2,3,4,5)) 형태로 넣으면 f(1,2,3,4,5)와 동일
매직 메소드
- 객체를 그냥 print 하면 메모리 주소값이 나오지만, def __str__(self)를 선언한 후 print 시 함수에서 설정한 출력문이 나옴
- __add__(self, other)의 경우 other는 자신이 아닌 더해지는 다른 객체를 가리킴
상속
- class A(B) : A class가 B class를 상속
- super().__init__ : 부모 클래스에서 생성한 것을 상속받아 사용
- super().메서드() : 부모 클래스의 메서드 사용
다형성
- 같은 부모 클래스의 상속을 받은 다른 자식 클래스가 같은 이름 메소드의 내부 로직을 다르게 작성 가능
- 기능이 비슷하면 함수 이름은 같게 하고 기능에 차이를 조금씩 둠
가시성, 캡슐화
- self.__item = [] : private 변수, 외부에서 접근 불가능
- 클래스 밖에서 객체.__item로 불러올 수 없음
@property def items(self): return self.__item # property decorator를 이용해 private 변수인 item에 접근 # 이 때 class 밖에서 items 함수를 items()가 아닌 items로 호출한다.
일등함수, 일급객체
- 변수나 데이터 구조에 할당이 가능한 객체
- map(f, ex) 처럼 함수 f를 파라미터로 사용 가능
decorator
- 함수 내 또 다른 함수를 inner function이라 하며, 이 함수를 return으로 반환하는 것을 closure라고 한다.
- decorator는 비슷한 목적의 다른 파라미터를 요구하는 함수 생성을 가능하게 한다.
- __pycache__: 컴파일 된 파일(기계어로 번역된 파일)
- Alias(별칭): import 모듈명 as alias
- package : 하나의 대형 프로젝트를 만드는 코드 묶음 (모듈, 폴더 등으로 구성)
- 리눅스에선 touch 파일명.py로 py파일 생성
- __init__.py 파일은 현재 폴더가 패키지임을 나타내줌
파일 내에 __all__ = [모듈 이름들] 및 from . import 모듈명 내용을 포함시킴- 패키지 폴더 자체를 실행(cmd에서 python 폴더명)시키면 __main__.py가 실행됨
- from .render import * # .render는 상대참조
- from ..render import * # ..render은 render의 부모 디렉토리로 이동 후 같은 경로의 다른 모듈 임포트 가능
- raise Exception (출력문) : raise 구문을 만나면 코드 종료 후 출력문 출력
- assert 예외조건 : 예외 조건이 False면 AssertionError 발생 후 코드 종료, True면 실행
File
- file.readlines() : 개행 기준으로 리스트에 저장
- read(): 전체 읽어옴
- readline(): readline을 실행할 때마다 한 줄씩 읽어옴
- write 모드는 파일을 새로 열게 됨(기존 파일을 불러와도 다 초기화하고 새로운 내용만 write), add는 기존 파일에 추가하는 것
- shutil.copy : 파일 복사
- pathlib: path를 객체로 다룸
- cwd = pathlib.Path.cwd()
cwd : 파일 주소 가리킴
cwd.parent : 한 단계 위 디렉토리
- Pickle: 메모리에 있는 데이터를 저장할 수 있음 (영속화)
- f = open('list.pickle', 'wb')
pickle.dump(데이터, f) # 데이터 저장- f = open('list.pickle', 'rb')
pickle.load(f) # 피클 load- class도 피클에 저장 가능
로깅
- logger = logging.getLogger("main") : 로깅 객체 생성
- logging.basicConfig(level=logging.DEBUG) : 기본 레벨을 디버그로 설정
- steam_handler = logging.FileHandler(파일명, mode='w')
logger.addhandler(steam_handler)
-> 로그를 파일로 기록함configparser
- 프로그램 실행 설정을 파일에 저장
- dict type 형태로 관리
- example.cfg 파일 형태
- import configparser 후 cfg = configparser.Configparser().read('example.cfg') 로 객체 생성 후 cfg[키] 형태로 json처럼 사용
argparser
- 프로그램 실행할 때 세팅 정보 저장
- 커맨드창에서 --a 형식으로 써서 값들을 넣어줌
보통 긴 것은 --, 짧은 것은 - 사용
ex) python arg_sum.py -a 10 -b 10- logging.Formatter(메세지 format) 을 지정해서 사용
ndarray
- 하나의 데이터 타입만 배열에 넣을 수 있음
- list는 데이터의 주소값이 저장되는 것이고, ndarray는 데이터 값이 그대로 들어감 (연산이 좋음)
- is 연산자는 메모리 위치를 비교함
같은 값을 비교할 때 list는 True, ndarray는 False (-5~256 사이의 값이어도 ndarray에선 주소값이 다름)- ndarray [1,4,5,8] 의 shape -> (4, )이며 vector를 표현
[[1,4,5,8]] -> (1, 4)- 1byte = 8bit, float 64(64bit) => 데이터 한 개당 8byte 할당
- rank=0인 array => scalar, 1 => vector, 2 => matrix , n => tensor
- shape: (쌓인 개수, 로우 개수, 칼럼 개수)
- nbytes: 데이터 byte 수를 반환
functions
- reshape: array shape 크기 변경, 원소 개수 동일
reshape(-1, n) : n개로 칼럼 개수 정하고 스스로 계산해서 row 개수도 선정한다. (n,-1)도 가능
reshape된 결과를 변수에 할당해야 변경된 matrix를 사용 가능- flatten: 다차원 array를 1차원으로 변환
- list에선 [i][j]로 인덱싱하지만 ndarray에선 [i, j]로 표기
- list와 달리 행, 열 부분 나눠서 슬라이싱 가능
a[1, 1:3] -> 1 row의 1~2 col- arange(30) : range(0, 30)으로 vector 생성
arange(0, 5, 0.5) : step이 0.5 (list에선 불가능)- np.empty(shape, dtype) : 메모리 공간을 잡지 않고 빈 값의 ndarray를 생성한다. 메모리 초기화가 안 돼서 빈 값에 다른 값이 들어갈 수 있음
- np.ones_like(array) : array의 크기만큼 ones matrix 반환 (zeros, empty도 가능)
- identity(n) : n 크기의 단위 행렬(1 대각 행렬) 생성
- eye(N, M, k): (N, M) 크기의 행렬에서 k번째부터 단위 행렬로 생성
k default = 0, 음수 인덱싱도 가능- diag(array): array의 대각 행렬 값만 추출해서 vector로 반환 eye처럼 k 사용 가능
- np.random.uniform(0, 1, 10): [0, 1)의 10 크기만큼 랜덤으로 float 값 array
- np.random.normal: 정규분포를 따르는 데이터 생성
- array.sum(): 어레이 총합
mean, var, std 다 가능- matrix에선 axis로 연산 방향 지정 가능 (axis = 0: 열방향, 1 : 축방향)
- 3order tensor에선 0: 채널 방향, 1: 열방향 , 2 : 축방향
- vstack(vertical stack): 밑에다 붙임
hstack(horizontal stack): 옆에다 붙임
np.vstack(a, b) 형태- np.concatenate((a, b) axis = 0) 형태
- b[np.newaxis, :] : b가 [5, 6]이면 축을 하나 추가해서 [[5,6]]으로 바꿈 (2, ) -> (1, 2)
- array * array -> 같은 위치 곱 (element-wise operation)
- array.dot(array) -> 행렬곱(내적)
- array.transpose() 또는 array.T : 전치
- matrix + scalar: broadcasting, 행렬 모든 element에 scalar 값만큼 더해줌 (뺄셈, 곱셈, 나눗셈, 몫, 제곱 등 연산 가능)
matrix + vector(vector를 matrix에 다 더해줌), vector + vector (모두 더하면서 matrix를 생성) 모두 가능- 대용량 계산에는 넘파이가 제일 빠름
boolean functions
- array = [0, 1, 2]일 때,
array > 1 -> [False, False, True] 반환
np.any(a > 1) -> 하나라도 True면 True 반환
np.all(a > 1) -> 모두가 all이면 True 반환
np.logical_and(a>0, a<3) : 두 bool list 비교 후 and 연산
np.logical_not(a) : not a
np.logical_or(b,c) : b, c or연산
- where 리턴값은 튜플
- np.where(a > 0, 3, 2) : a > 0은 bool array이고 True 영역에는 3이, False 영역에는 2가 들어감
np.where(a>5) : 5> 식을 만족하는 index 값 반환- np.isnan(a) : 메모리 값이 존재하지 않는지 반환
- np.isfinite(a) : 메모리 초과하는 값인지 반환(np.Nan이랑 np.Inf에는 False)
- np.argmax(a) : a array에서 가장 큰 값의 index 반환
np.argmin(a) : 가장 작은 값
matrix에 넣으면 axis 방향대로 가장 큰 값들 또는 작은 값들 뽑아낸 벡터로 반환- a.argsort() : 가장 작은 값부터 인덱스를 뽑아줌
a[np.argmax(a)]를 해주면 인덱스가 아닌 최대값 자체를 뽑아냄- condition = a>3 # bool array
- a[condition]: 3보다 큰 값만 포함하는 어레이 반환 (condition에서 True)
a = [2,4,6,8]
b = [0,0,1,3,2,1]
a[b]: [2,2,4,8,6,4] a.take(b)와 같은 결과 # fancy index
b matrix를 index로 취급
a[b, c]처럼 matrix 형태도 가능- loadtxt, savetxt로 txt파일 불러오고 저장 가능
np.save : pickle 형태로 데이터 저장 / np.load로 불러오기
np.save(파일명.npy, arr = 객체) / np.load(file = 파일명.npy)
groupby
- df.groupby('team')['points'].sum() : 데이터프레임의 team 칼럼을 기준으로 묶어 point의 총계를 구하며, 시리즈 반환
- df.groupby(['team', 'year'])['points'].sum() 처럼 두 개의 칼럼으로 그룹핑도 가능
이 때 인덱스는 두 개이며, 이를 hierarchical index라고 함 (데이터가 한 개고 인덱스가 두 개 이상이어도 시리즈)- index를 찍으면 multi index라고 나오며, 멀티인덱스 시리즈에 unstack() 함수를 적용하면 matrix 형태로 풀어줌
또는 reset_index를 적용하면 multi index를 single index로 변환 ***- hierarchical index에 sort_index(level=) 함수를 적용하면 level 파라미터의 인덱스를 기준으로 정렬
- h_index.sum, std, mean 등 level을 지정해줘서 구할 수 있음
- grouped = df.groupby('team') 처럼 사용하면 grouped는 튜플을 원소로 가짐
(그룹 이름, 그룹 데이터(dataframe type) )
for name, group in grouped의 형태로 사용 가능- grouped.get_group(그룹 이름)을 사용하면 특정 그룹의 그룹 데이터를 뽑아낼 수 있음
- grouped.agg(sum): 그룹별 칼럼 데이터의 총합을 구함 (np.mean, sum, std 등 사용 가능)
- grouped[칼럼명].agg([sum, mean, std]) 와 같이 그룹 내 특정 칼럼의 여러 통계치를 뽑아낼 수도 있음
- score = lambda x: (x - x.mean() ) / x.std() # 정규화
grouped.transform(score)
transform은 개별 데이터에 score 함수를 적용한다. 따라서 평균, 표준편차가 각 팀마다 구해지고 개별 데이터에 정규화가 적용됨
- df.groupby('team').filter(lambda x: len(x) > 3) : team으로 그룹핑 했을 때 행이 3개 초과하는 데이터만 추출, dataframe 반환
- df.['team'].value_counts 를 사용하면 각 팀별 데이터 개수 파악 가능
- groupby('team', as_index=False)를 하면 team을 인덱스로 쓰지 않음
reset_index와 같은 기능merge
- pd.merge(df1, df2, on='칼럼명') : on 칼럼명 기준으로 같은 값 merge, 공통된 데이터만 추출
- pd.merge(df1, df2, left_on='칼럼명1', right_on='칼럼명2') : 두 칼럼 이름이 다를 때 따로 지정해줘서 merge
- pd.merge(df1, df2, on='칼럼명', how = 'left') : left join, 왼쪽엔 있고 오른쪽에 없으면 nan값으로 fill
- how = 'right'으로 하면 right join, how = 'outer'으로 하면 full join, default는 inner join
index기준으로 join하려면 merge(right_index = True, left_index = True)- merge할 때 칼럼 이름이 동일하면 _x, _y가 붙음. 이 때 한 칼럼은 drop해줘야 함
- pd.concat([df1, df2], axis = ) 형태로 사용, axis = 0이면 밑으로, 1이면 옆으로 붙임
axis = 0은 df1.append(df2)와 같은 형태