2024. 4. 20. 21:30ㆍ공부
파이썬은 최근 많이 사용되고있는 언어로, 웹, 빅데이터, 통계, AI등 많은 분야에서 사용되고있는 언어중 하나다.
파이썬의 장점은 우선 뭐니뭐니해도 쉬운난이도가 아닐까 싶다. 파이썬을 제외하고 많이 사용하는 언어들인 C, Java를 보면 이해가 쉽다.
만약 C언어에서 Hello World를 출력하고자한다면, 아래와 같은 코드를 작성해야한다.
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
하지만 파이썬 에서 같은동작을 수행하고자 한다면?
print("Hello World!");
이렇게 하나의 문장으로 출력이 된다.
물론 다양한 라이브러리를 보유하고 있어 다른언어에서 처리하기 어려운 문제들을 쉽게 처리할수있기도하다.
오늘은 그중에서도 파이썬에서 데이터 정제를 위해 주로 사용하는 Numpy(넘파이)에 대해 알아보고자한다.
Numpy 배열
파이썬에는 여러자료형값을 한번에 처리할수있는 '리스트'가 존재한다.
다양한 자료형을 넣을수있어 자료구조적으로는 활용도가 매우높지만, 데이터를 처리하는 기능이 부족하고, 속도도 빠르지않다. 그래서 보통 사용할때는 리스트를 사용하는것이 아닌 넘파이를 사용한다.
넘파이를 사용하기위해서는 우선 파이썬 코드 내부에서 라이브러리를 사용하겠다고 선언해주어야한다.
ex) C++ 에서의 #include <iostream> 처럼 말이다.
import numpy as np
선언은 위와같이 하게되는데, 명령어를 해석해보면, "numpy라이브러리를 삽입할건데 이것의 약어를 np라고 칭하고 사용할꺼야" 가된다.
이것이 무슨 소리냐 하면, 만약 numpy라이브러리에 array라고하는 배열을 생성하는 함수가 있어서 이를 사용하고자 할때, numpy뒤에 as np를 제외하고 작성한다면, numpy.array()라고 작성해야한다.
이것을 위의 코드대로 실행하게된다면 np.array()로 코드를 입력하여도 numpy.array()와 동일한 동작을 수행하는것이다.
np.array([1,2,3,4])
#위의 코드와 아래 코드는 같은 동작을 수행한다.
numpy.array([1,2,3,4])
list = [1, 2, 3, 4]
np.array(list)
#또한 리스트와같은 ndarray가 아닌객체를 ndarray로 변환할때도 사용한다
이는 코드의 단축을 위해 사용된다.
이렇게 생성된 배열은 ndarray객체로 생성되며 배열에 대한 정보를 확인하기 위해서는 아래와 같은 함수를 적용하여 정보를 확인할수있다.
a.shape
# 배열의 형태(shape)를 표현하며, 행렬을 튜플의 형식으로 표현한다
# a와 같은 일차원 배열의 경우, (3,)처럼 해당 열,혹은 행의 개수만 나타낸다
b = np.array([[1,2,3], [4,5,6]])
b.shape
# b의경우 2행 3열을 가진 2차원배열이므로 b.shape의 결과는 (2,3)으로 나타난다.
a.ndim
# dim은 dimension의 약어로 차원의 개수를 나타낸다. a의경우 1차원 배열이므로 1을 return
# b의 경우 2행, 2차원 배열이므로 2를 return 한다.
a.dtype
# 생성한 ndarray객체의 데이터타입을 반환한다. a의 경우 int타입이 들어가있으므로,
# int64 타입을 return 하게 된다(int64는 ndarray의 기본값으로 2^64를 나타낸다.)
a.itemsize
# 배열 원소의 크기를 반환한다. a의경우 int64 이므로, 64/8 = 8이 된다.
a.size
# ndarray객체에 들어있는 원소의 갯수를 반환한다. a에서는 2,3,4이므로 3을 반환한다.
그렇다면 여기서 설명하는 ndarray객체는 무엇인가?
ndarray는 Numpy에서 사용하는 N차원 배열 객체(Numpy N-dimension array)로 list와 큰차이점은 하나의 데이터타입만 사용할수있지만, 다양한 연산이 가능하다는 장점이있다.
또한 ndarray객체에는 객체간 사칙연산이 가능하며, 연산을 적용할경우, 행렬의 자료형과 형태(shape)가 일치할때만 가능하며, 각 위치의 원소별로 사칙연산을 적용하게 된다.
a = np.array([20, 25, 30])
b = np.array([3, 2, 1])
# 연산을 진행할때는 형태와 자료형이 일치해야한다.
# 연산은 각 원소별로 진행된다.
a + b
# array([23, 27, 31])
a - b
# array([17, 23, 29])
a * b
# array([60, 50, 30])
a / b
# array([ 6.666666667, 12.5, 30.])
또한 배열에 대해서는 스칼라 곱셉, 덧셈도 가능하다.
a * 10
# ndarray객체에 대해 스칼라곱을 적용하면, 모든원소에 10을 곱해져서 결과를 출력하게된다
ndarray를 생성하는 또다른 방법
ndarray를 생성할때는 np.array와 같이 요소를 모두 지정해서 생성하는 방법도 존재하지만, 다른 여러함수를 사용해서 지정된 ndarray를 생성할수있다.
np.zeros((3, 3))
# 3행 3열의 행렬을 생성하고 행렬의 모든값을 0으로 초기화한다.
np.ones((3, 3))
# 3행 3열의 행렬을 생성하고 행렬의 모든값을 1로 초기화한다.
np.full((3, 3), 100)
# 3행 3열의 행렬을 생성시 모든 값을 100으로설정한다.
np.eye(4)
# 4행 4열의 크기의 단위행렬을 생성한다.
# 단위행렬 : 대각선의 성분이 1로 초기화한다. 나머지 성분은 0으로 초기화한다.
# 행렬 생성시 열을 추가하고싶은경우, 내부 변수에 추가하고싶은 열만큼 추가로 지정해준다.
# np.eye((4),10)
# 4행을 단위행렬로 생성한후, 10열로 생성한다. 4열이후에는 숫자가 기입되지않는다.
np.arange(0, 10, 2)
# 0부터 10까지의 숫자를 2개씩 띄워 배열을 초기화한다.
# return : array([0, 2, 4, 6, 8])
# arange함수는 정수와 실수 모두에 적용가능하다.
np.linspace(0, 10, 5)
# 0 부터 10까지의 숫자를 5개만큼 동일한 간격으로 배열을 생성한다.
# return : array([0, 2.5, 5, 7.5, 10.])
np.linspace(0, 10, 4)
# return : array([0, 3.333333, 6.666666667, 10.])
생성한 다차원 배열에 원소를 삽입하는 방법
insert()를 사용하면, 배열의 중간에 원하는 원소를 삽입할수있다.
a = np.array([1, 3, 4])
np.insert(a, 1, 2)
# return : array([1, 2, 3, 4])
위의 코드에서는 insert()를 사용해서, 1,3,4였던 a배열에 2를 추가해서 1,2,3,4를 반환하고있다.
insert의 내부 변수는 다음과 같다
insert(삽입하고자하는 배열, 배열내에 삽입할위치, 삽입할값)
위 기준에 따라 a에대한 insert를 해석해보면,
insert(a, 1, 2) : a : 삽입하고자하는배열, 1 : 삽입할위치(index는 0부터시작 따라서 1다음 위치에 들어간다.), 4 : 삽입할값
다음과 같이 해석된다.
2차원 배열의 경우에는 새로운 변수 axis값이 추가된다. axis값은 삽입할값을 행기준으로 삽입할것인지, 열기준으로 삽입할것인지를 결정하는 변수다. 아래를 예시로 들면 axis = 0인경우 행에서 2번째 위치에값을 삽입하며, axis = 1인경우 열에서 2번째 열에 삽입하게 된다.

이와 비슷한 방식으로 동작하는것이 flip함수다. flip함수는 axis에 따라 지정된 축방향으로 원소를 뒤집어주는함수다.

리스트와 넘파이 배열의 차이점
리스트는 데이터를 단순히 나열되어있는것으로 생각한다. 이는 아래코드에서 확인할수있다.
Num_a = [10, 30, 50] #숫자 리스트 a
Num_b = [20, 40, 60] #숫자 리스트 b
이전에 넘파이에서 활용했던대로 리스트간의 연산을 수행해보겠다. 결과는 어떻게 되었을까?
두값이 합쳐져 있을까?
결론부터 말하자면 아니다. 이는 다음 코드를 통해확인할수있다.
List_sum = Num_a + Num_b
List_sum
# return : [10, 30, 50, 20, 40, 60]
리스트에서는 원소들을 더하지않고 합쳐진것을 확인할수있다.
이는 리스트 원소들이 특별한 의미를 가지고 있는것이 아닌, 단순히 저장만 되어있는것이기때문이다.
그렇기에 단일원소들을 제대로 활용하고싶다면 ndarray로 변환하거나, 최초선언을 ndarray로 선언하여 사용하는것이 바람직하다.
인덱스 슬라이싱과 reshape()
인덱스 슬라이싱과 reshape()(행렬을 형태를 변경한다.)는 Numpy라이브러리에서 arange와 더불어 가장 많이 사용하는 함수들로(인덱스 슬라이싱은 함수가 아니긴하지만, 많이 사용해 넣게되었다.) 데이터를 가공할때 많이 사용된다.
인덱스 슬라이싱
인덱스 슬라이싱은 큰행렬에서 특정 위치에 존재하는 작은행렬을 꺼내고 싶을때 사용하게된다. 이에대한 예시는 아래와 같다.
a = np.arange(0, 100, 10)
# return array([0, 10, 20, 30, 40, 50, 60, 70, 80, 90])
# 0<= a < 100범위에서 10단위로 표현하는 ndarray생성
a[0:10]
# return array([0, 10, 20, 30, 40, 50, 60, 70, 80, 90])
# 0번째 부터 10번째 인덱스에있는값들을 가져오지만, 배열의 원소들은 9개이므로 마지막값인 90까지 출력한다
a[4: -1]
# return array([40, 50, 60, 70, 80])
# 4번째부터 마지막값 바로 전까지 슬라이싱하여 출력한다.
# :가 뒤에있는경우 해당인덱스부터 뒤에 지정되어있는값까지 슬라이싱한다.
# -1은 슬라이싱을 실행할때 마지막인덱스 앞에서부터 슬라이싱을 실행한다.
a[2:8:3]
# return array([20, 50])
# 2번째 인덱스부터 8번째 인덱스이전까지 3개씩 건너뛰어가며 인덱스를 슬라이싱하겠다.
# 위와같은 형태는 자주사용된다.
a[:7]
# return array([0, 10, 20, 30, 40, 50, 60])
# 첫번째부터 7번째 인덱스 이전까지 슬라이싱한다.
# :7과 같이 앞에 아무런숫자없이 ':'기호와 숫자가 있다면, 처음인덱스부터 해당지점까지 슬라이싱한다는 의미이다.
a[:7:2]
# return array([0, 20, 40, 60])
# 첫번째부터 7번째 인덱스 이전까지 2개씩 건너뛰어 가며 슬라이싱한다.
a[7::2]
# return array([70, 90])
# 7번째 인덱스부터 마지막 인덱스 까지 2개씩 건너뛰어가며 슬라이싱한다.
a[::2]
# return array([0, 20, 40, 60, 80])
# 첫번째 인덱스 부터 마지막 인덱스까지, 2개씩 건너뛰어가며, 슬라이싱한다.
# 앞에 어떠한 숫자없이 ':'기호만 존재하는경우 첫번째부터 마지막인덱스까지 모두 슬라이싱한다.
a[5:1:-1]
# return array([50, 40, 30, 20])
# 5번째 인덱스부터 1번째 인덱스 이전까지, 역순으로 슬라이싱한다.
# ':-1' 과 같이 -1에 ':'기호가 같이 붙은경우 슬라이싱을 역순으로 진행한다.
# ** ':-1' 가없는경우 해당 슬라이싱은 실행되지않는다.
a[::-1]
# return array([90, 80, 70, 60, 50, 40, 30, 20, 10, 0])
# 첫번째 인덱스부터 마지막 인덱스까지 역순으로 출력한다.
1차원 행렬에대한 슬라이싱은 위와 같다, 하지만 2차원배열의 슬라이싱의 경우 행에대한 슬라이싱과 열에대한 행을 추려서 뽑을수있어 1차원배열 슬라이싱에서 행에대한 슬라이싱과 열에대한 슬라이싱이 추가된다.
a = np.arange(0,16).reshape(4,4)
# 0부터 15까지의 원소를 가지는 4x4의 행렬을 생성
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15]])
a[0]
# return array([0, 1, 2, 3])
# 2차원이지만, 행에대한 슬라이싱값만을 넣는경우 기본값은 해당 행의 모든열값을 출력한다.
a[1:]
# return array([[ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15]])
# 1번째 인덱스 행부터 마지막행까지의 열값을 출력한다
a[1:,2]
# return array([ 6, 10, 14])
# 1번째 인덱스 행부터 마지막행까지의 2번째 열의값만을 출력한다.
a[1:3,1:3]
# return array([[ 5, 6],
# [ 9, 10]])
# 1번째 인덱스 행부터 3번째 이전인덱스행(1,2행)의 1번째부터 3번째 이번 인덱스 열의 값을 출력한다.
a[::2,::2]
# return array([[ 0, 2],
# [ 8, 10]])
# 첫번째 인덱스부터 마지막 인덱스까지 두개씩 건너뛴 행에서
# 첫번째부터 마지막 인덱스까지 두개씩 건너뛴 열값을 출력한다.
a[::2]
# return array([[ 0, 1, 2, 3],
# [ 8, 9, 10, 11]])
# 첫번째 인덱스부터 마지막 인덱스까지 두개씩 건너뛴 행에서
# 모든 열값을 출력한다.
a[:,2]
# return array([ 2, 6, 10, 14])
# 2번째 인덱스의 열값에 대해서 모든 행값을 출력한다.
넘파이에서의 인덱스 슬라이싱은 순차적으로 적용되는것이 아닌, 각각의 행에대해서 별도로 적용되기 때문에, 다양한 형태의 슬라이싱이 가능하다.
또한 넘파이 슬라이싱은 인덱스 슬라이싱만 가능한것이 아닌, 논리 슬라이싱역시 가능하다.
논리 슬라이싱은 인덱스가 아닌, 배열에 특정 조건을 주어 불리언 배열을 생성하거나, 조건에 부합하는 원소만 추출할수있다.
a = np.arange(0,16).reshape(4,4)
# return
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15]])
a % 2 == 0
# return
# array([[ True, False, True, False],
# [ True, False, True, False],
# [ True, False, True, False],
# [ True, False, True, False]])
# a에있는 모든 원소를 나누었을때 나머지가 0인 원소들(짝수)에대해 불리언 배열을생성한다
a[a % 2 == 0]
# return array([ 0, 2, 4, 6, 8, 10, 12, 14])
# 배열의 대괄호([])에 조건을 입력하면 불리언배열이 아닌 원소 배열을 반환받는다.
# 이를 통해 원하는 원소들을 조건을 지정해서 뽑아낼수있다.
reshape(행렬변환)
reshape(n,m) 함수는 ndarray객체의 행렬을 변환할수있다.
a = np.arange(0,4)
다음과 같이 원소가 4개있는 ndarray가 있다고 가정해보자 이를 출력한다면 결과는 아래와같을것이다.
array([0, 1, 2, 3])
그런데 이와같은 배열을 2차원배열로 변경해야할때가 있을것이다. 그럴때 reshape를 사용해서 변경할수있다.
a = a.reshape(2,2)
위와 같이 실행하면 결과는 아래와 같다.
array([[0,1],
[2,3]])
a.shape
# return (2,2)
행렬을 확인하기위해 a.shape를 적용해보면 아래와 같은 결과가 나오는것 역시 확인할수있다.
이때 주의해야할점은, reshape를 사용할때는 행렬의 원소 갯수가 변경할 행렬과 일치해야한다.
ex) 1차원 행렬에서 2x2 행렬 변환하고자 한다면, 4개의 원소만 존재해야한다.
기타 넘파이의 함수들
max(),min()
배열의 원소중 최대값, 최소값을 찾아낸다.
a = np.array([20, 25, 10])
a.max()
# return 25
a.min()
# return 10
mean()
배열의 평균값을 구한다.
a.mean()
# return 18.33333333.....
sort()
배열을 오름차순으로 정렬한다.
a.sort()
# return array([25, 20, 10])
# 오름차순정렬
a[::-1]
# return array([10, 20, 25])
# 내림차순 정렬
astype()
배열의 자료형타입을 변경한다.
a.astype(np.float64)
# return array([20., 25., 10.])
flatten
다차원 배열이 2차원이상의 배열을 1차원 배열로 변경한다 (평탄화)
a = np.array([[1,1], [2,2], [3,3]])
a.flatten()
# return array[1, 1, 2, 2, 3, 3]
T(transpose)
2차원 행렬에대해 전치행렬을 적용한다.
a = np.array([[1,1], [2,2], [3,3]])
# 전치행렬은 .T속성을 이용해 변환한다.
a.T
# return
# array([[1,2,3],
# [1,2,3]])
# 3x2 행렬을 2x3행렬로 변환한다.
append()
인자로 입력된 두배열 합친다. (axis를 명시하지않으면, 기본값으로 1차원 배열로 변환)
a = np.array([1, 2, 3])
b = np.array([[4, 5, 6], [7, 8, 9]])
np.append(a, b)
# return array([1, 2, 3, 4, 5, 6, 7, 8, 9])
# 축을 명시하는 경우 axis를 지정해주면 특정축으로 합치게하여 배열을 유지할수있게한다
np.append([a], b, axis = 0) [a]를 통해 2차원 배열로 만들어야한다.
# return
# array([[1, 2, 3],
# [4, 5, 6],
# [7, 8, 9]])
matmul()
넘파이에서 제공하는 행렬곱 함수
전치행렬.T 과 같이 @를 사용하면 matmul()함수를 더욱 쉽게 사용할수있다.
a = np.array([[1, 2], [3, 4]])
b = np.array([[10, 20], [30, 40]])
np.matmul(a, b)
# return
# array ([[ 70, 100],
# [150, 200]])
a @ b # == np.matmul(a,b)
concatenate
두개의 배열을 리스트처럼 결합하는 함수
a = np.array([[1, 2, 3, 4],
[5, 6, 7, 8]])
b = np. array([[10, 11, 12, 13],
[14, 15, 16, 17]])
np.concatenate((a,b)) #a 배열의 뒤로 b배열의 원소를 삽입한다.
# return
# array([[1, 2, 3, 4],
# [5, 6, 7, 8],
# [10, 11, 12, 13],
# [14, 15, 16, 17]])
vstack()
두개의 배열을 수직(Vertical)방향으로 결합한다. concatenate함수는 vstack함수로 구현할수있다.
또한 배열과 리스트도 결합할수있으며, 배열3개를 한번에 결합하는것도 가능하다.
hstack()
두개의 배열을 수평(Horizontal)방향으로 결합한다.
axis = 0(행)의 값이 동일한 경우에만 결합을 수행할수있다.
'공부' 카테고리의 다른 글
| [파이썬] 내가보려고 정리하는 파이썬 : Matplotlib (0) | 2024.04.21 |
|---|---|
| [파이썬]내가보려고 정리하는 파이썬 : Pandas (0) | 2024.04.21 |
| [그래픽스]Viewing (1) | 2024.04.19 |
| [그래픽스]GeoMetry(지오메트리) (0) | 2024.04.17 |
| [그래픽스]02-1. 그래픽 처리과정2 (0) | 2024.04.14 |