📊 Numpy 라이브러리


📌 Python Numpy

  • numpy란? (Numeric Python)
    고성능의 수치계산과 선형대수학을 위해 제작된 Python 라이브러리
    벡터 및 행렬 연산에 매우 편리한 기능 제공

  • np.array()
    - Array 객체를 생성하는 메소드
    - np.array([1,2,3,4], dtype=np.데이터타입)









📊 List와 Array 차이


📌 내부 요소의 데이터타입 차이

  • 내부 요소 차이
    - list : 내부 요소는 다양한 데이터타입을 갖는다.
    - array : 내부 요소는 하나의 데이터타입으로 일관된다.
ss = ['tom', 'james', 'oscar', 1]
print(ss, type(ss))     # 리스트 타입
# ['tom', 'james', 'oscar', 1] <class 'list'>

ss2 = np.array(ss)      # array 형으로 형변환
print(ss2, type(ss2))   # numpy의 ndarray 클래스 타입
# ['tom' 'james' 'oscar' '1'] <class 'numpy.ndarray'>
  • Array 내부 요소 데이터타입의 자동 형변환 특징
    - array 내부에는 하나의 데이터타입으로 통일되야 한다.
    - 서로 다른 타입이 존재하면 하나의 타입으로 자동 형변환 발생
    💡 array 데이터타입 우선순위 : [ int < float < str ]


📌 메모리 사용 방법 차이

  • 메모리 사용 차이

- list : 내부의 개별 요소개별 인스턴스로 따로 저장됨
ex) 내부 요소 개수가 8개라면 각 요소는 8개의 다른 저장소 블록에 따로 저장됨

- array : 내부의 개별 요소하나의 인스턴스로 저장됨
ex) 내부 요소 개수가 8개라면 전체가 하나의 저장소 블록에 저장됨

li = list(range(1, 10))
print(li)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
print('[0, 1] 인덱스 주소 : ', id(li[0]), id(li[1]))
# [0, 1] 인덱스 주소 :  2458407495984 2458407496016
# -> 두개의 인스턴스 주소가 서로 다르다.

num_arr = np.array(li)
print(num_arr)
# [1 2 3 4 5 6 7 8 9]
print('[0, 1] 인덱스 주소 : ', id(num_arr[0]), id(num_arr[1]))
# [0, 1] 인덱스 주소 :  2458454022768 2458454022768
# -> 두개의 인스턴스 주소가 동일하다.
  • list 특징 (개별 인스턴스로 저장)
    - 장점 : 유연성이 높다.
    - 단점 : 비효율적 메모리 사용

  • array 특징 (하나의 인스턴스로 저장)
    - 장점 : 메모리의 효율적 사용
    - 단점 : 유연성이 낮다.


📌 연산자 적용 결과 비교

  • 연산자 적용 결과
    - list : 리스트에 연산자 적용 시, 리스트를 문자열로 처리함
    - array : 내부 요소 각각에 연산자 적용 가능
print(li * 2)      # 문자열 곱하기 연산자로 인식
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print(num_arr * 2) # 각 요소에 곱하기 연산이 적용
# [ 2  4  6  8 10 12 14 16 18]









📊 Array 클래스의 필드 기능


📌 Array 내장 함수

  • 내장 함수
    - type(arr) : 해당 객체의 타입 반환
num_arr = np.arange(1, 10)
print(type(num_arr))
# 타입 : <class 'numpy.ndarray'>


📌 Array 필드 변수

  • 필드
    - arr.dtype : 내부 요소 타입 반환
    - arr.shape : 몇차원의 어떤 형태를 갖고 있는지를 반환
    - arr.ndim : 행의 개수 반환
    - arr.size : 내부 요소의 전체 개수 반환
num_arr = np.arange(1, 10)

print(num_arr.dtype)
# int32
print(num_arr.shape)
# (9,)	=> 1차원의 9개 요소를 갖는 벡터
print(num_arr.ndim)
# 1		=> 행개수
print(num_arr.size)
# 9		=> 행개수 * 열개수









📊 Array 메소드


📌 (n x n) array 만들기

  • (n x n) array
    - np.zeros((행,열)) : 행 * 열을 0으로 채운 array
    - np.ones((행,열)) : 행 * 열을 1로 채운 array
    - np.full((행,열), fill_value=값) : 행 * 열을 값으로 채운 array
    - np.eye((행,열)) : 행 * 열에서 주대각을 1로 채우고 나머지는 0으로 채움
print( np.zeros((2,2)) )    # 전체 0으로 채우기
#	[[0. 0.]
#	 [0. 0.]]

print( np.ones((2,2)) ) # 전체 1로 채우기
#	[[1. 1.]
#	 [1. 1.]]

print( np.full((2,2), fill_value=7) )   # 전체 7로 채우기
#	[[7 7]
#	 [7 7]]

print( np.eye(3) )  # 주대각 = 1, 나머지 = 0
#	[[1. 0. 0.]
#	 [0. 1. 0.]
#	 [0. 0. 1.]]


📌 np.random 모듈의 메소드 사용

  • np.random을 이용한 균등분포와 정규분포의 차이
    - np.random.seed(값) : 매번 동일한 값을 추출하기 위한 시드값 설정
    - np.random.rand(추출개수) : 0 ~ 1 균일분포 표준정규분포 난수 추출
    - np.random.randn(추출개수) : 표준편차1, 평균0의 가우시안 표준정규분포 난수 추출
    - np.random.randint(시작, 끝) : 지정 범위 사이의 정수형 난수 1개 추출
np.random.seed(0)
print( np.random.rand(5) )    # 균등분포 (0 ~ 1)
# [0.5488135  0.71518937 0.60276338 0.54488318 0.4236548 ]

print( np.random.randn(5) )   # 가우시안 정규분포
# [-0.84272405  1.96992445  1.26611853 -0.50587654  2.54520078]

print( np.random.randint(10) )
# 5

# 참고 사항
print(np.mean(np.random.rand(5000)))     # 균등분포에 대해 확률변수 개수가 많아질수록 0.5에 수렴함
# 0.503702391541436
print(np.mean(np.random.randn(5000)))    # 정규분포에 대해 확률변수 개수가 많아질수록 0에 수렴함
# 0.003258670460904787


📌 range 함수를 이용한 연속형 변수 묶기

  • np.arange() 메소드
    - list(range(1, 10) : 1~9 까지의 값을 갖는 list
    - np.array(range(1, 10)) : 1~9 까지의 값을 갖는 array
    - np.arange(1, 10) : 1~9 까지의 값을 갖는 array
print(list(range(1, 10)))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

print(np.array(range(1, 10)))
# [1 2 3 4 5 6 7 8 9]

print(np.arange(1, 10))
# [1 2 3 4 5 6 7 8 9]


📌 Array의 주소값 치환과 값 복사

  • 치환과 array 복사
    - 치환 : 치환 제공자 변수의 주소를 대상자에게 전달해 같이 사용
    두 변수는 연결되어 있음

- np.copy(Array객체) : Array객체의 구조와 내부 값만 복사해서 사용
두 변수는 상호 독립적인 변수이다.

a = np.array(1, 5)		# (4,) 1차원 Vector

# 치환
a1 = a

# 객체 복사
a2 = np.copy(a)

a[0] = 100
print(a1)	# [100, 2, 3, 4]
print(a2)	# [1, 2, 3, 4]









📊 Array 인덱싱, 슬라이싱


📌 1차원 Array 인덱싱, 슬라이싱

  • 인덱싱
    arr[인덱스]

  • 슬라이싱
    arr[인덱스1 : 인덱스2]

  • 전체 요소 순서 바꾸기
    arr[::-1]
a = np.array([1,2,3,4,5])

# 인덱싱
print(a[-4])	# 2

# 슬라이싱
print(a[1:4])	# [2 3 4]
print(a[1:4:2])	# [2 4]
print(a[1:])	# [2 3 4 5]

# 요소 순서 바꾸기
print(a[::-1])	# [5 4 3 2 1]


📌 2차원 Array 인덱싱, 슬라이싱

  • 인덱싱 특징
    - 기본적으로 높은 차원에서의 인덱싱차원축소를 하는 역할을 한다.

  • 인덱싱
    - arr[0] : 0번째 행의 차원축소된 1차원 벡터를 추출
    - arr[0][0] : 0번째 행인 1차원 벡터에서 0번째 요소를 추출
    - arr[0, 0] : 0번째 행인 1차원 벡터에서 0번째 요소를 추출
    - arr[[0]] : 0번째 행을 차원축소하지 않고 원본 그대로 추출
a = np.array([[1,2,3],[4,5,6]])		# (2, 3) 2차원 Matrix

print(a)
# [[1 2 3]
#  [4 5 6]]

print(a[0])
# [1 2 3]

print(a[0][0])
print(arr[0, 0])
# 1

print(a[[0]])
# [[1 2 3]]


  • 슬라이싱 특징
    - 슬라이싱만 사용하면 차원축소가 되지 않는다.

  • 슬라이싱
    - arr[0:2] : 0행~1행까지 2차원 Matrix 추출
    - arr[:1, :2] : 0행, 0~1열까지의 2차원 Matrix 추출
a = np.array([[1,2,3],[4,5,6]])		# (2, 3) 2차원 Matrix

print(arr[0:2])
#	[[1 2 3]
#	 [4 5 6]]

print(arr[:1, :2])
#	[[1 2]]


  • 인덱싱 + 슬라이싱
    - a[0, 0:2] : 0행의 차원축소된 1차원 벡터에서 0~1번째까지의 1차원 벡터 추출
a = np.array([[1,2,3],[4,5,6]])		# (2, 3) 2차원 Matrix

print(a[0, 0:2])
#	[1 2]


📌 Sub Array

  • Sub Array란?
    슬라이싱을 이용해 일부 데이터만 치환하여 만든 Array를 의미함
a = np.array([[1,2,3],[4,5,6]])

a1 = a[:2, 1:]     # 일부만 치환 (Sub Array)
print(a1)
#	[[2 3]
#	 [5 6]]

a1[0,0] = 100       # 0행 0열의 값 수정
print(a)            # a Array도 수정됨
#	[[1 100 3]
#	 [4 5 6]]

print(a1)
#	[[100 3]
#	 [5 6]]









📊 Array 차원 구조 변경


📌 reshape() 메소드

  • reshape()
    - np.array의 형태를 변환한다.
    - np.arange(1, 10).reshape(3, 3) : 1~10까지의 1차원 벡터를 3행3열 2차원 Matrix로 변경
y = np.arange(5, 9).reshape(2, 2)   # (4, ) 벡터 -> (2, 2) 행렬 array
print(y, y.shape, y.size)
#	[[5 6]
#	 [7 8]]
# (2,2)
# 4









📊 Array 연산


📌 Numpy 연산

  • numpy 연산 특징
    - Python 내장 함수보다 속도가 빠름

  • numpy 연산 종류
    - np.sum(), np.mean(), ....


📌 더하기 연산 np.sum()

  • np.sum()
    - 두 Array에서 동일한 인덱스 요소들에 더하기 연산 적용
# product Sum : 동일한 인덱스에 있는 요소끼리 더하기 연산
print(x + y)
print(np.add(x, y))
# => 동일한 결과 제공
  • np.cumsum()
    - 하나의 Array에서 인덱스가 높아질수록 누적합을 적용해 추출
x = np.arange(5).reshape(2,2)

print(np.cumsum(x))     # 누적합
# [ 1.  3.  6. 10.]


📌 빼기 연산 np.substract()

  • np.substract()
    - 두 Array에서 동일한 인덱스 요소들에 빼기 연산 적용
# product Substract : 동일한 인덱스에 있는 요소끼리 빼기 연산
print(x - y)
print(np.subtract(x, y))


📌 곱하기 연산 np.multiply()

  • np.multiply()
    - 두 Array에서 동일한 인덱스 요소들에 곱하기 연산 적용
# product multiply : 동일한 인덱스에 있는 요소끼리 곱하기 연산
print(x * y)
print(np.multiply(x, y))
  • np.cumprod()
    - 하나의 Array에서 인덱스가 높아질수록 누적곱을 적용해 추출
x = np.arange(5).reshape(2,2)

print(np.cumprod(x))    # 누적곱
# [ 1.  2.  6. 24.]


📌 나누기 연산 np.divide()

  • np.divide()
    - 두 Array에서 동일한 인덱스 요소들에 나누기 연산 적용
# product divide : 동일한 인덱스에 있는 요소끼리 나누기 연산
print(x / y)
print(np.divide(x, y))


📌 평균, 표준편차

  • np.mean()
    - 하나의 Array 내부 요소의 평균을 추출

  • np.std()
    - 하나의 Array 내부 요소의 표준편차를 추출
print(np.mean(x))

print(np.std(x))


📌 행렬곱 (벡터간 내적 연산)

  • np.dot()
    - 두 Array에 내적 연산 적용

  • 조건
    - 첫번째 Array의 열개수두번째 Array의 행개수동일해야 한다.
    ex) (N x M) Array(M x Z) Array 간의 행렬곱


📌 1차원 x 1차원 행렬곱

  • 1차원 x 1차원 행렬곱
v = np.array([9, 10])		# (2,) 1차원 벡터
w = np.array([11, 12])		# (2,) 1차원 벡터

print(v * w)				# 요소별 곱셈 -> 1차원 벡터
# [99 120]

print(np.dot(v, w))			# v[0]*w[0] + v[1]*w[1] -> 스칼라값
# 219


📌 2차원 x 1차원 행렬곱

  • 2차원 x 1차원 행렬곱
x = np.arange(5).reshape(2, 2)		# (2,2) 2차원 Matrix
v = np.array([9, 10])				# (2,) 1차원 벡터

print(np.dot(x, v))		# [ (x[0][0]*v[0] + x[0][1]*v[1]) (x[1][0]*v[0] + x[1][1]*v[1]) ]
# [29. 67.]


📌 2차원 x 2차원 행렬곱

  • 2차원 x 2차원 행렬곱
x = np.arange(5).reshape(2, 2)			# (2,2) 2차원 Matrix
y = np.arange(5, 9).reshape(2, 2)		# (2,2) 2차원 Matrix

print(np.dot(x, y))  	# 일반 matrix 간의 행렬곱과 같이 처리
#	[[19. 22.]
#	 [43. 50.]]


📌 집합 연산

  • np.unique()
    - 하나의 Array 내부 요소의 중복을 제거한 결과 반환
name1 = np.array(['tom', 'james', 'tom', 'oscar'])

print(np.unique(name1))     # 중복 제거
# ['james' 'oscar' 'tom']

  • np.union1d()
    - 두개의 1차원 Array의 합집합 추출
name1 = np.array(['tom', 'james', 'tom', 'oscar'])
name2 = np.array(['tom', 'page', 'john'])

print(np.union1d(name1, name2))         # 합집합
# ['james' 'john' 'oscar' 'page' 'tom']

  • np.intersect1d()
    - 두개의 1차원 Array의 합집합 추출
name1 = np.array(['tom', 'james', 'tom', 'oscar'])
name2 = np.array(['tom', 'page', 'john'])

print(np.intersect1d(name1, name2))     # 교집합
# ['tom']

print(np.intersect1d(name1, name2, assume_unique=True))     # 교집합 (중복 허용)
# ['tom' 'tom']


📌 전치 연산

  • transpose() or T
    - 하나의 Array에서 행과 열을 바꾼다.
x = np.arange(5).reshape(2, 2)			# (2,2) 2차원 Matrix

print(x.T)
#	[[1. 3.]
#	 [2. 4.]]

print(x.transpose())
#	[[1. 3.]
#	 [2. 4.]]

📌 Broadcast 연산

  • Broadcast
    - 크기가 다른 배열 간의 연산을 하면 작은 배열이 큰 배열의 크기를 자동으로 따라감
x = np.arange(1, 10).reshape(3,3)   # (3,3) 행렬
y = np.array([1,0,1])   			# (3, )

print(x + y)	# Broadcast 연산으로 (3, 3) 행렬 출력
#	[[2 2 4]
#	 [5 5 7]
#	 [8 8 10]]









📊 Array 행, 열 추가


📌 Array에 열 추가

  • **np.c[]**_
    - np.c[2차원 Array객체, 추가할 1차원 벡터 열]_
    : 2차원 Array 객체에 추가할 1차원 벡터를 열에 추가
a1 = np.eye(3)			# (3,3) 2차원 Matrix
add_col = np.array(4)	# (3,) 1차원 벡터

a2 = np.c_[a1, add_col]
# [[1. 0. 0. 1.]
#  [0. 1. 0. 2.]
#  [0. 0. 1. 3.]]


📌 Array에 행 추가

  • **np.r[]**_
    - np.c[2차원 Array객체, 추가할 1차원 벡터 행]_
    : 2차원 Array 객체에 추가할 1차원 벡터를 행에 추가
a1 = np.eye(3)			# (3,3) 2차원 Matrix
add_col = np.array(4)	# (3,) 1차원 벡터

a2 = np.r_[a1, add_col]		# 행에 [1 2 3] 추가
# [[1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]
#  [1. 2. 3.]]


📌 1차원 벡터를 2차원 행렬로 확장

  • reshape() 메소드 사용
    - 1차원 벡터를 reshape() 메소드를 통해 2차원 Matrix로 확장
a = np.array([1,2,3])   # (3,) 1차원 벡터
print(a.reshape(3, 1))		# (3, 1) 2차원 Matrix로 확장
#	[[1]
#	 [2]
#	 [3]]

print(a.reshape(1, 3))		# (1, 3) 2차원 Matrix로 확장
# [[1 2 3]]


📌 1차원 벡터에 값 추가 및 삭제

  • np.append() 메소드
    - np.append(벡터, 추가할벡터, axis=0) : 새로운 1차원 벡터 추가
    - axis 속성 : 0(행기준[Default]), 1(열기준)
a = np.array([1,2,3])   # (3,) 1차원 벡터
b1 = np.append(a, [4,5], axis=0)     # axis = 0(행기준[Default]), 1(열기준)
print(b1)
# [1 2 3 4 5]

  • np.insert() 메소드
    - np.insert(벡터, 인덱스, 추가할벡터, axis=0) : 인덱스에 새로운 1차원 벡터 추가
a = np.array([1,2,3])   # (3,) 1차원 벡터
b1 = np.insert(a, 1, [4,5], axis=0)
print(b1)
# [1 4 5 2 3]

  • np.delete() 메소드
    - np.delete(벡터, 인덱스[list or 숫자형]) : 인덱스에 속한 데이터를 벡터에서 빼기
a = np.array([1,2,3])   # (3,) 1차원 벡터
b1 = np.delete(a, 1)
print(b1)
# [1 3]

b2 = np.delete(a, [1,2])
print(b2)
# [1]


📌 2차원 벡터에 값 추가 및 삭제

  • np.append() 메소드
    - np.append(벡터, 추가할벡터) : 차원 축소하여 마지막에 추가
    - np.append(벡터, 추가할벡터, axis=0) : 차원 유지. 마지막 행에 추가
    - np.append(벡터, 추가할벡터, axis=1) : 차원 유지. 마지막 열에 추가
a = np.array([1,2,3])   # (3,) 1차원 벡터

b1 = np.append(a, [4,5,6])
print(b1)
# [1 2 3 4 5 6]

b1 = np.append(a, [4,5,6], axis=0)
print(b1)
#	[[1 2 3]
#	 [4 5 6]]

b1 = np.append(a, [4], axis=1)
print(b1)
#	[[1 2 3 4]]

  • np.insert() 메소드
    - np.insert(행렬, index, 새로운요소) : 차원 축소하여 추가
    - np.insert(행렬, index, 새로운 요소, axis=0) : 차원 유지. 행 기준 처리
    - np.insert(행렬, index, 새로운 요소, axis=1) : 차원 유지. 열 기준 처리
aa = np.arange(1, 10).reshape(3,3)  # (3,3) 2차원 행렬

print(np.insert(aa, 1, 99))
# [ 1 99  2  3  4  5  6  7  8  9]

print(np.insert(aa, 1, 99, axis=0))
# [[ 1  2  3]
#  [99 99 99]
#  [ 4  5  6]
#  [ 7  8  9]]

print(np.insert(aa, 1, 99, axis=1))
# [[ 1 99  2  3]
#  [ 4 99  5  6]
#  [ 7 99  8  9]]

  • np.insert() 메소드
    - np.insert(벡터, 인덱스, 추가할벡터, axis=0) : 인덱스에 새로운 1차원 벡터 추가
a = np.array([1,2,3])   # (3,) 1차원 벡터
b1 = np.insert(a, 1, [4,5], axis=0)
print(b1)
# [1 4 5 2 3]

  • np.delete() 메소드
    - np.delete(벡터, 인덱스[list or 숫자형]) : 인덱스에 속한 데이터를 벡터에서 빼기
a = np.array([1,2,3])   # (3,) 1차원 벡터
b1 = np.delete(a, 1)
print(b1)
# [1 3]

b2 = np.delete(a, [1,2])
print(b2)
# [1]









📊 Array 행, 열 추가


📌 Array에 열 추가

  • **np.c[]**_
    - np.c[2차원 Array객체, 추가할 1차원 벡터 열]_
    : 2차원 Array 객체에 추가할 1차원 벡터를 열에 추가
a1 = np.eye(3)			# (3,3) 2차원 Matrix
add_col = np.array(4)	# (3,) 1차원 벡터

a2 = np.c_[a1, add_col]
# [[1. 0. 0. 1.]
#  [0. 1. 0. 2.]
#  [0. 0. 1. 3.]]


profile
데이터 사이언티스트를 목표로 하는 개발자

0개의 댓글