본문 바로가기

STUDY

파이썬 스터디 1주차(머신러닝)

https://book.naver.com/bookdb/book_detail.nhn?bid=16238302 

 

파이썬 머신러닝 완벽 가이드

자세한 이론 설명과 파이썬 실습을 통해 머신러닝을 완벽하게 배울 수 있습니다!《파이썬 머신러닝 완벽 가이드》는 이론 위주의 머신러닝 책에서 탈피해 다양한 실전 예제를 직접 구현해 보면

book.naver.com

1. 파이썬 기반의 머신러닝과 생태계 이해

01. 머신러닝의 개념

 

머신러닝 : 애플리케이션을 수정하지 않고도 데이터를 기반으로 패턴을 학습하고 결과를 예측하는 알고리즘 기법.

- 지도학습(Supervised Learning), 비지도학습(Un-supervised Learning), 강화학습(Reinforcement Learning)으로 나뉨.

- 데이터에 매우 의존적임(Garbage In, Garbage Out)

 

 

02. 파이썬 머신러닝 생태계를 구성하는 주요 패키지

 

머신러닝을 가장 빠르게 익히는 방법 중 하나는 다른 사람이 만든 소스코드를 이해하고 그것을 자기 것으로 흡수하기 위해 많은 코드를 스스로 만들어 보는 것.

 

 

03. 넘파이

 

넘파이NUMerical PYthon : 파이썬에서 선형대수 기반의 프로그램을 쉽게 만들 수 있도록 지원하는 대표적인 패키지. 많은 머신러닝 알고리즘이 넘파이 기반으로 작성되어 있기 때문에 이를 이해하는 것이 파이썬 기반의 머신러닝에서 매우 중요함.

  • Ndarray : 넘파이의 기반 데이터 타입. 다차원(Multi-dimension)배열을 쉽게 생성하고 다양한 연산을 수행할 수 있다.
array1 = np.array([1, 2, 3]) #1차원
array2 = np.array([['a', 'b', 'c'], ['d', 'e', 'f']]) #2차원
array3 = np.array([[1, 2, 3]]) #2차원

 

 

- ndarray.shape는 ndarray의 차원과 크기를 튜플(tuple) 형태로 나타내줌. array1은 (3, ) array2는 (2, 3) array3은 (1, 3)

 

list1 = [1, 2.0, 3.0]
array1 = np.array(list1)
#[1. 2. 3.] 출력. dtype으로 확인하면 float64(실수)

array1_=array1.astype('int32')
#[1 2 3] 출력. dtype으로 확인하면 int32(정수)

 

- ndarray 내의 데이터 값은 숫자, 문자, 불 값 모두 가능하지만 연산 특성상 같은 데이터 타입만 가능. 만약 다른 데이터 유형이 섞여 있는 리스트를 ndarray로 변경하면 데이터 크기가 더 큰 타입으로 형 변환이 일괄 적용됨.

- ndarray 내의 데이터값 타입 변경은 astype() 매서드 사용. 메모리를 절약해야 할 때 주로 이용함.

 

array1 = np.arange(1, 8, 2) #[1 3 5 7] 출력(1부터 7(=8-1)까지 2씩 띄움)
array2 = np.zeros((2, 2)) #2x2의 shape를 가진, 0으로만 구성된 ndarray
array3 = np.ones((3, 2), dtype='float32') #3x2의 shape를 가진, 1.0으로만 구성된 ndarray

 

- arange()는 array를 range()로 표현하는 것. range와 유사하게 start값을 부여하는 것 또한 가능.

- zeros()는 인자로 튜플 형태의 shape를 입력하면 모든 값을 0으로 채운 shape의 ndarray를, ones()는 모든 값을 1로 채운 shape의 ndarray 반환(default dtype은 float).

 

array1 = np.arange(8)
array2 = array1.reshape(2, -1)
#[[0 1 2 3]
# [4 5 6 7]]출력. 4개의 1차원과 호환되는 로우 2개에 맞는 칼럼 개수는 4이므로 2x4

 

- reshape()는 ndarray를 특정 차원 및 크기로 변환하나 변경이 불가능한 경우에는 오류가 발생함. -1을 인자로 사용해 원래의 ndarray와 호환되는 shape로 변환 가능.

- reshape(-1, 1)은 원본 ndarray가 어떤 형태이더라도 2차원이면서 1개의 칼럼을 가진 ndarray로 변환시킴

 

  • 인덱싱
array1 = np.arange(1, 10).reshape(3, 3) #1부터 9까지를 3x3으로 배열
array1[0, 0] = 9 
array1[-1, -1] = 1
#(0, 0)위치의 1을 9로, (-1, -1)위치의 9를 1로 바꿈

 

1. 특정 데이터만 추출: 원하는 위치의 인덱스 값 지정

- 인덱스는 0부터 시작하며, 마이너스 기호를 사용하면 맨 뒤에서부터 데이터 추출

- ndarray 내의 데이터 값 수정 또한 가능

- 2차원 데이터의 경우 콤마로 분리된 로우와 칼럼 위치 인덱스로 접근해 추출

axis 0로우 방향의 축, axis 1칼럼 방향의 축. 디폴트값은 axis 0.

 

array2 = np.arange(1,10).reshape(3,3)
array2[:2, :] #인덱스값 1인 2행까지([[1 2 3] [4 5 6]])
array2[2] #인덱스값 2인 3행([7, 8, 9])

 

2. 슬라이싱(Slicing): 연속된 인덱스상의 ndarray 추출

- ':' 기호 이용. `시작:종료`면 시작~종료-1 위치의 데이터 ndarray를 반환.

- 시작, 종료 인덱스 생략 가능(시작 생략은 처음부터, 종료 생략은 마지막까지, 둘다 생략은 처음~마지막까지)

- 2차원 ndarray에서 뒤 인덱스(칼럼)를 없애면 1차원 ndarray반환(로우 축(axis 0)의 로우만 반환). array2[2]를 array3라 저장하고 array3.shape로 확인해보면 (3, )!

 

array3 = np.arange(1,10).reshape(3,3)
array3[[0,1]] #((0,:), (1,:))인덱싱 적용. [0:2, :]과 동일.

array4 = np.arange(9) #0부터 8까지 9개의 숫자로 구성된 array
array4[[True,False,True,True,False,False,False,False,True]] #array([0, 2, 3, 8])

 

3. 팬시 인덱싱(Fnacy Indexing): 일정 인덱싱 집합을 리스트나 ndarray로 지정해 해당 위치의 데이터 ndarray 반환

 

4. 불린 인덱싱(Boolean Indexing) : True/False 값 인덱싱 집합 기반으로 True 인덱스 위치의 데이터 ndarray 반환

- []내에 조건문을 그대로 기재해 사용하거나 결과로 반환된 ndarray 객체 자체를 넣음.

 

  • 행렬
array1 = np.array([3, 6, 1, 5, 9])
array2 = np.sort(array1)[::-1] #[9 6 5 3 1]
array3 = array1.sort() #array3은 None임. array1을 프린트 해 보면 [1, 3, 5, 6, 9]

 

- 넘파이 행렬 정렬은 np.sort()와 ndarray.sort() 크게 두 가지

- np.sort()는 원 행렬은 그대로 두고 정리된 행렬을 반환, ndarray.sort()는 원 행렬을 정렬하고 반환값 None

- 둘다 기본적으로는 오름차순 정렬. 내림차순으로 하기 위해서는 [::-1] 사용

 

array1 = np.array([[3, 2], [1, 6]])
array1_ = np.sort(array1, axis=0) #[[1 2], [3 6]]

array2 = np.array([3, 2, 9])
array2_ = np.argsort(array2) #[1 0 2], type은 numpy.ndarray

 

- 행렬이 2차원 이상일 경우 axis 축 값 설정을 통해 로우 방향(axis=0) 또는 칼럼 방향(axis=1)으로 정렬도 가능

- 원본 행렬이 정렬되었을 때 기존 원본 행렬의 원소에 대한 인덱스가 필요할 때 np.argsort()이용. 반환값은 ndarray형.

- 내림차순 정렬 시 원본 행렬의 인덱스를 구하는 것도 np.argsort()[::-1]

 

names = np.array(['Zeus', 'Oner', 'Faker', 'Gumayusi', 'Keria'])
awards = np.array([0, 0, 19, 0, 7])

sort_awards = np.argsort(awards)[::-1] #names[sort_awards]하면 ['Faker' 'Keria' 'Gumayusi' 'Oner' 'Zeus']

 

- 넘파이의 ndarray는 메타 데이터를 가질 수 없으므로 실제 값과 그 값이 뜻하는 메타 데이터를 별도의 ndarray로 각각 가져야만 함.

 

A = np.array([[2021, 2022], [2020, 2019]])
B = np.array([[1], [-2]])

dots = np.dot(A, B) #[[-2023], [-2018]] (=2021-4044, 2020-4038)
#np.transpose(A)하면 [[2021, 2020], [2022, 2019]]

 

- 행렬 내적 = 행렬 곱. np.dot()

- 두 행렬 A와 B의 내적은 A의 로우(행)와 B의 칼럼(열)의 원소들을 순차적으로 곱한 뒤 그 결과를 모두 더한 값.

- 전치행렬 : 원 행렬에서 행과 열 위치를 교환한 원소로 구성한 행렬. np.transpose()로 쉽게 구할 수 있음.

 

 

04. 데이터 핸들링 - 판다스

 

판다스 : 행과 열로 이루어진 2차원 데이터를 효율적으로 가공/처리할 수 있는 다양하고 훌륭한 기능 제공!

  • DataFrame : 판다스의 핵심 객체. 여러 개의 행과 열로 이루어진 2차원 데이터를 담는 데이터 구조체.

- Index와 Series이해가 중요 :: Index는 개별 데이터를 고유하게 식별하는 Key값. Series는 칼럼이 하나뿐인 데이터 구조체로, Index를 key값으로 가짐

- DataFrame은 여러 개의 Series로 이루어졌다고 할 수 있음

- read_csv()는 csv뿐만 아니라 어떤 필드 구분 문자 기반 파일 포맷도 DataFrame으로 변환 가능. 인자인 sep에 해당 구분문자를 입력해주면 됨

- read_csv()엔 로드하려는 데이터 파일의 경로를 포함한 파일명을 입력해 주어야 함

- read_csv()는 별 다른 파라미터 지정이 없다면 파일의 맨 처음 로우를 칼럼 명으로 인지하고, 칼럼으로 변환함

- 모든 DataFrame 내의 데이터는 생성되는 순간 고유의 Index 값을 가지게 됨. 

- DataFrame.head()는 DataFrame의 맨 앞에 있는 N개(파라미터)의 로우 반환, DataFrame.shape()는 DataFrame의 행과 열을 튜플 형태로 반환

 

- DataFrame.info()로 칼럼 타입, Null 데이터 개수, 데이터 분포도 등의 메타데이터 조회 가능

 

- describe()는 칼럼별 수치형 데이터의 정보만 알려줌(object 타입의 칼럼은 출력에서 제외)

 

-DataFrmae의 []연산자 내부에 칼럼 명을 입력하면 Series 형태로 특정 칼럼 데이터 세트 반환.

- value_counts는 반환된 Series에 호출했을 때 해당 칼럼 값의 유형과 건수 확인 가능. 반환값 역시 Series

- Series는 Index와 단 하나의 칼럼으로 구성된 데이터 세트. 모든 Series와 DataFrame은 인덱스를 가짐

- 인덱스는 고유성이 보장된다면 의미 있는 데이터값 할당도 가능!

 

  • DataFrame-리스트-딕셔너리-넘파이ndarray 상호변환

 

- DataFrame은 파이썬 리스트, 딕셔너리, 넘파이 ndarray 등으로부터 생성될 수 있으며 반대로 DataFrame도 파이썬 리스트, 딕셔너리, 넘파이 ndarray 등으로 변환될 수 있음

- DataFrame은 기본적으로 행과 열을 가지는 2차원 데이터 : 2차원 이하의 데이터들만 DataFrame으로 변환될 수 있음

- 딕셔너리를 DataFrame으로 변환하는 경우 딕셔너리의 Key는 칼럼명, Value는 칼럼 데이터.

- DataFrame 객체의 values를 이용하면 DataFrame을 넘파이 ndarray로 변환. 이 ndarray에 tolist()를 호출하면 리스트로 변환

- DataFrame 객체에 to_dict('list')를 써주면 딕셔너리 값을 리스트형으로 반환

 

- DataFrame의 칼럼 데이터 세트 생성과 수정도 []연산자로 가능.

- 넘파이 ndarray에 상숫값을 할당할 때 모든 ndarray값에 일괄 적용되는 것처럼, Series에 상숫값을 할당할 때에도 Series의 모든 데이터셋에 일괄 적용됨.

 

- DataFrame의 데이터 삭제는 drop()메서드 활용. drop() 메서드 원형은

 

DataFrame.drop(labels=None, axis=0, index=None, columns=None, level=None, inplace=False, errors='raise')

 

- axis 0은 로우방향 축, axis 1은 칼럼 방향 축.

- axis 0이면 DataFrame은 자동으로 labels에 오는 값을 인덱스로 간주.

- 주로 axis 1으로 설정하고 칼럼을 드롭하는 경우가 많음. aixs 0으로 설정할 때에는 이상치를 삭제하는 경우.

- Inplace=False는 삭제된 결과 DF만 반환, inplace=True는 자기 자신의 DF 데이터를 삭제하고 None을 반환. 그러므로 inplace=True로 설정한 반환값을 다시 원 DF객체로 할당하면 안 됨! 객체변수가 None이 되기 때문.

 

  • Index

- Index : DataFrame, Series의 레코드를 고유하게 식별하는 개체.

- 실제 값은 넘파이 1차원 ndarray. values 속성으로 ndarray 값을 알 수 있음.

- 한 번 만들어진 DataFrame, Series의 Index 객체는 함부로 변경 불가

- Index는 오직 식별용임. Series 객체에 연산 함수를 적용할 때 Index는 연산에서 제외됨.

- DataFrame 및 Series에 reset_index()를 수행하면 새로운 숫자 인덱스를 할당하며 기존 인덱스는 'index'라는 새 칼럼명으로 추가됨. 이는 인덱스를 연속된 int 숫자형 데이터로 만들기 위해 사용

- Series에 reset_index()를 적용하면 기존 인덱스가 칼럼으로 변환되어 Series가 아닌 DataFrame이 반환됨

- reset_index() 파라미터로 drop=True를 설정하면 기존 인덱스가 새 칼럼('index')으로 추가되지 않고 삭제되며, Series에 적용할 경우 Series로 유지됨

 

  • 데이터 셀렉션

- 넘파이와 유사하면서도 달라 혼동하기 쉬움

 

- 넘파이의 []연산자 : 행 위치, 열 위치, 슬라이싱 범위 등을 지정해 데이터를 가져옴

- 판다스의 []연산자 : (DataFrame 뒤에서) 칼럼 명 문자(칼럼 명 리스트 객체), 인덱스로 변환 가능한 표현식만 사용해 데이터를 가져옴

(ix는 다루지 않겠습니다)

- DataFrame.loc[] : 칼럼 명칭 기반 인덱싱 연산자. 슬라이싱을 적용할 경우 종료 값 까지 포함(숫자형이 아닐 수 있기 때문). 불린 조건을 입력해주면 불린 인덱싱 가능

- DataFrame.iloc[] : 칼럼 위치 기반 인덱싱 연산자. 행과 열 값으로 int, int형 슬라이싱, 팬시 리스트 값을 주어야함. 불린

불가

- DataFrame 인덱스 값은 명칭 기반 인덱싱(=명칭 기반과 위치 기반 데이터 형이 서로 같을 경우 명칭 기반이 우선함)

- 복합조건도 사용 가능(and는 &, or는 |, Not은 ~). 개별 조건을 ()로 묶고 복합 조건 연산자 사용

 

  • 정렬, Aggregation, Groupby

- sort_values() : DataFrame과 Series의 정렬을 위해 사용. SQL의 order by와 유사.

- 주요 입력 파라미터는 by(해당 칼럼으로 정렬 수행), ascending(=True는 오름차순, =False는 내림차순), inplace.

- 여러 개의 칼럼으로 정렬하려면 by에 리스트 형식으로 칼럼 입력

 

- DataFrame에서의 aggregation 함수 적용은 SQL의 aggregation 함수 적용과 유사하나 바로 aggregation을 호출할 경우 모든 칼럼에 적용해줌.

- 특정 칼럼에만 aggregation 함수를 적용하기 위해선 대상 칼럼만 추출해 aggregation( df[['승률', '픽률']].mean() )

 

- DataFrame의 groupby()는 SQL의 groupby 키워드와 유사

- 입력 파라미터 by에 칼럼을 입력하면 대상 칼럼으로 groupby하며 DataFrameGroupBy라는 다른 DataFrame 반환

- DataFrameGroupBy에 aggregation을 호출하면 groupby() 대상 칼럼을 제외한 모든 칼럼에 aggregation 적용

- 특정 칼럼만 aggregation을 적용하려면 DataFrameGroupBy 객체에 해당 칼럼들을 필터링 한 뒤 aggregation 적용

- 여러 aggregation 함수를 적용할 경우, 함수 명을 DataFrameGroupBy의 agg()내에 인자로 입력해 사용

( df.groupby('티어')['픽률'].agg([max,min]) )(티어에 따라 groupby된 객체의 픽률 max, min)

- 각각의 처리가 필요한 경우 agg()내 입력값으로 딕셔너리 형태의 aggregation 적용 칼럼과 함수 입력

 

  • Missing Data 처리하기

- 결손 데이터(Missing Data) : 칼럼에 값이 없는 NULL인 경우. 넘파이의 NaN이 해당. 평균, 총합 등의 함수 연산에서 제외되어 머신러닝 알고리즘으로 처리되지 않아 때문에 다른 값으로 대체해야 함. 

- NaN 여부를 확인하는 API는 isna(), NaN 값을 다른 값으로 대체하는 API는 fillna()

- DataFrame.isna()는 모든 칼럼 값이 NaN인지 아닌지 불린으로 알려줌. sum()으로 결손 데이터 개수를 구함

- DataFrame.fillna()는 결손 데이터를 ()안의 인자로 대체해 반환함. default는 inplace=False

 

  • apply lambda

- 판다스에서는 apply 함수에 lambda 식을 결합해 DataFrame이나 Series 레코드별로 데이터 가공 가능

- lambda는 함수 선언과 함수 내 처리를 한 줄로 쉽게 변환한 식.

 

year = [-3, -2, -1]
years = list(map(lambda x : x+2022, year))
print("Keria", years, "수상 축하합니다") #Keria [2019, 2020, 2021] 수상 축하합니다

 

- 위 식에서 ':' 왼쪽인 x는 입력 인자, ':' 오른쪽인 x + 2022는 입력인자의 계산식(반환값). 

- lambda 식에서 여러 값을 입력 인자로 받아야 할 때는 map()함수 사용

- lambda 식은 if else를 지원하나 if 절은 if 식보다 반환값이 먼저, else는 else가 먼저 옴

- if~else만 지원하고 if, else if, else는 지원x

 

 

05. 정리

 

- 넘파이와 판다스는 머신러닝 학습을 위해 정복해야 할 매우 중요한 요소

- 머신러닝 프로그램을 직접 만들면서 이해하고 모르는 부분을 찾아보는 게 학습을 위한 제일 빠른 방법

- 실제로 머신러닝 모델 생성/예측 수행에 있어 알고리즘보다 데이터 전처리, 가공/추출이 더 많은 비중을 차지함.

- 사이킷런에 대한 이해도 중요하나 다양한 패키지에 대한 이해 역시 매우 중요

 


1.24 기준 한번 정리해 보았는데, 그래도 여전히 필사의 느낌이 남아있네요. 추후에 또 간추릴 수 있으면 좋겠습니다.