Python_데이터 분석 (3) numpy

조아연·2024년 9월 11일

numpy


[초기 설정]

  • 터미널 내 pip install numpy 입력
  • 터미널에서 설치가 되지 않는다면 cmd 에서 설치
Downloading numpy-2.1.0-cp312-cp312-win_amd64.whl (12.6 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.6/12.6 MB 8.3 MB/s eta 0:00:00

1) numpy 정의

Numpy(numerical python) : 수학 연산을 위한 파이썬 패키지

  • 수학적 연산을 위해 주로 활용하는 인공지능 분야 (다차원 배열)
  • 과학적 계산을 수행하는데 필수적인 라이브러리

=> 넘파이 배열(ndarray) 다차원 배열 연산이 우수

 

  • nadrray는 넘파이 배열의 약자

  • 파이썬의 리스트도 존재하나, 넘파일 배열의 연산속도가 리스트보다 빠르고 메모리가 효율적

  • java나 c++보다 속도는 느리지만 위와 같은 라이브러리가 있기에 빠른 연산이 가능

 

import numpy as np

n = np.array([1,2,3,4,5])
print(type(n))  # TYPE :? 데이터의 타입을 뽑아냄

"""
<class'numpy.ndarray'>
n이라는 변수는 numpy의 ndarry 라는 type를 가짐
ndarray의 n은 정수를, d는 dimension(차원)을 의미
즉, n 차원 배열 객체를 의미하므로, 2차원, 3차원 이상의 다차원 배열을 생성할 수 있음을 유추 할 수 있음
"""


print(n.ndim)  #n 객체의 차원 (1)  / 1차원 배열이라는 뜻
print(n.shape) # n 객체 배열 모양 (5,)  / 튜플 형태, 행과 열을 이루는 원소들의 갯수에 대한 정보를 튜플로 표시 
print(n.size) # n 객체의 크기 (5)     / 몇개의 원소가 있는지 (size)
print(n.itemsize) # n 객체 원소들의 메모리 크기 (8)  /  itemsize는 각 원소들의 메모리 크기를 byte로 표시 
print(n.dtype) # (int64)    / dtype는 원소들의 자료형 2*숫자 2의 64승 

- 넘파이 특징

"""
[넘파이 특징]
- 배열 안에 하나의 자료형만 사용(리스트는 여러 자료형, 정수, 문자 등)
- 성분별(elementwise) 연산이 가능함
- 반복문없이 데이터 배열에 대한 처리가 가능
- 리스트 대비 연산 속도가 빠르고 메모리 효율적인
- 정료한 브로드캐스팅 가능함

[넘파이배열 ndarry와 list의 차이점]
1. 리스트와 달리 넘파이 배열은 같은 유형의 자료형만 담을 수 있음
==> 연산속도의 차이가 발생할 수 있음


[리스트는 형식없이 가능]
list_arr = [1,'a',5.0]
print(list_arr)

[넘파이에서는]
결과 : ['1','a','5.0'] 
==> 자료형이 문자로 통일해서 나옴

바로계산 가능
print(list_a1 + list_a2)
결과 : [1,2,3,4]

2. 성분별(element-wise) 연산 및 반복문 없이 데이터 처리가 가능
리스트 덧셈은 두개의 리스트를 append 

list_a1 = [1,2]
list_a2 = [3,4]

print(list_a1+list_a2)
결과:[1,2,3,4]

넘파이는 배열의 덧셈 지원
arr 1 = nparray([1,2])
arr 2 = nparray([3,4])
arr1+arr2 = 4,6


넘파이 배열은 성분별 연산, 반복문 없이 연산 가능
수치계산에 적합하게 나옴

ist_a = [1,2,3,4,5]
list_a = [i+1 for i in list_a]
print(list_a)

np_r = np.array([1,2,3,4,5])
np_r += 1
print(np_r)

결과 : [2,3,4,5,6]      # list_a
        [2 3 4 5 6]    # np_r


3. 연산 속도가  빠름
넘파일 배열은 메모리 블록에 데이터를 연속적으로 저장
원소를 조회하는 인덱싱과 슬라이싱이 빠르고 효율적
(리스트는 분산된 메모리 공간에 원소 저장)

배열의 개별원소를 처리하는 연산에서 리스트는 반복문이나 map과 같은 함수 사용해여해서 느림
넘파이는 벡터화 연산을 지원하기에 배열 연산속도가 빠름

같은 크기의 넘파일 배열과 리스트 배열에서 모든 원소에 1씩더하는데 걸린시간 비교해보면 넘파일 배열이 압도적으로 빠름


4. 리스트 대비 메모리 효율적임
무슨 자료형인지에 대해 따로 표시할필요 없음, 
데이터 타입을 포함하는 오버헤드가 없다 라고 표현

넘파이 상당 부분은 c언어나 포트란과 같은 언어로 작성되어 있어서 기본적으로 메모리 효율성이 뛰어남

넘파일 배열은 리스트와 달리 하나의 자료형만 보관할 수가 있어서 원소가 무슨 자료형인지 따로 표시할 필요가 없음
(이를 데이터 타입을 포함하는 오버헤드가 없음이라고 표현)

# 용량비교

import sys

lst_a = [i for i in range(1000000)]
np_a = np.arange(1000000)

print(sys.getsizeof(lst_a), sys.getsizeof(np_a))

[결과]
8448728 8000112

같은 크기의 배열이라도 넘파이의 배열의 메모가 더 작다
inport sys 모듈을 통해 알아 볼 수 있음

broadcasting(브로드캐스팅)
넘파이에서 서로 다른 모양(shape)의 배열도 일정 조건을 만족하면 연산할 수 있는데, 이 똑똑한 기능을 브로드캐스팅이라고 함

[브로드캐스팅 조건]
1. 원소가 하나인 배열은 어떤 배열이나 브로드캐스팅이 가능
2. 하나의 배열이 1차원 배열인 경우, 브로드캐스팅이 가능
2-1. 두 배열이 모두 2차원이면, 브로드캐스팅이 불가 


"""

- 하나의 자료형만 담을 수 있음

# 선언 필요 없는 리스트
# 결과 : [1, 'a', 5.0]

list_arr = [1, 'a', 5.0]
print(list_arr)


import numpy as np

list_arr = [1, 'a', 5.0]
np_arr = np.array(list_arr)
print(np_arr)


# 결과 : ['1' 'a' '5.0']

"""
넘파이 배열에서는 하나의 자료형만 담을 수 있기 때문에 
정수, 실수 문자열 데이터가 함께 입력되면 모든 원소를 문자열로 통일시킴.
"""

- 넘파이는 배열의 덧셈 지원

# 리스트 덧셈은 두 개의 리스트를 append
list_a1 = [1,2]
list_a2 = [3,4]

print(list_a1 + list_a2)
# 결과 : [1, 2, 3, 4]


# 넘파이는 배열의 덧셈 지원 
arr1 = np.array([1,2])
arr2 = np.array([3,4])
print(arr1 + arr2)
# 결과 : [4 6]

- 넘파이 배열은 반복문 없이 연산 가능

# 넘파이 배열은 반복문 없이 연산이 가능
list_a = [1,2,3,4,5]
list_a = [i+1 for i in list_a]
print(list_a)

np_r = np.array([1,2,3,4,5])
np_r += 1
print(np_r)

"""
[2, 3, 4, 5, 6] 3  # list_a
[2 3 4 5 6]        # np_r

리스트의 모든 원소에 1씩 더하려면 반복문이나 map 함수가 필요하지만, 
넘파이 배열은 반복문 없이 배열의 연산이 가능함.

"""

- 넘파이 내 arange() 메서드

# a range의 예제

import numpy as np

np_range = np.arange(1,10,2)
print(np_range)   # 1부터 10까지 2의 칸을 줘서
# [1 3 5 7 9]

"""
연속되거나 일정한 패턴으로 증감하는 원소를 가진
Numpy 배열을 생성하는 경우에는 arange() 메서드 이용
for의 range와 비슷
range 정수만 가능하지만 arange 는 실수도 가능

np_array = nparange(0.1, 1.0, 0.1)
print(np_array)

"""

- 넘파이 용량 비교하기

# 용량 비교하기

import numpy as np
import sys

lst_a = [i for i in range(1000000)]
np_a = np.arange(1000000)

print(sys.getsizeof(lst_a), sys.getsizeof(np_a))
# sys.getsizeof: sys 모듈에 포함된 함수로, 객체의 메모리 사용량을 반환

"""
8448728 8000104

=> 같은 크기의 배열이라도 넘파이 배열의 메모리가 더 작다.
"""

- 넘파이 내 저장된 데이터 타입 비교하기

import numpy as np

a = np.array([2,3,4])
print(a)
# [ 2 3 4]

print(a.dtype)  # 원소들의 자료형
# int64

b = np.array([1.2,3.5,5.1])
print(b.dtype) 
# float64 : 소수점 이하 값을 포함하는 숫자를 저장할 수 있는 데이터 타입

# dtype : 배열에 저장된 요소의 데이터 타입을 정의

- 넘파이 차원 및 타입 확인

import numpy as np

np_t1 = np.array([[1,2,3],[4,5,6]])
print(np_t1)
print(np_t1.ndim)  # 차원
print(np_t1.shape)  # 배열의 형태 출력 
print(np_t1.size)  #n 객체의 크기
print(np_t1.itemsize) #n 객체의 원소들의 메모리 크기
print(np_t1.dtype)   # 배열에 저장된 요소의 데이터 타입을 정의
 
""" [[1 2 3]
 [4 5 6]]
-  2차원 적으로 뽑아서 노출 

2
- 차원 노출

(2, 3)
- 2행 3열

6
- 객체의 크기 (6개 2*3개)

8
- 객체의 원소들의 메모리 크기
- 4byte

int64 
- 64bit


연산의 속도와 메모리 크기를 고려하여 1byte, int8 로 데이터 타입을 
변경하는 것이 유리함.

 """

- 넘파이 자료형 변경 방법

import numpy as np

np_t1 = np.array([[1,2,3],[4,5,6]]).dtype = "int8"
print(np_t1.itemsize)  # N 객체의 원소들의 크기
print(np_t1.dtype)

"""
i
int 8 
"""

np_t1 = np.array([[1,2,3],[4,5,6]])
np_t1 = np_t1.astype('int8')
print(np_t1.itemsize)
print(np_t1.dtype)

"""
1
int 8 

자료형 변경방법 위, 아래 2가지
1. 배열 생성과 동시에 자료형을 지정하거나
2. astpye() 함수를 이용하는 방법

"""

- 넘파이로 배열 생성

import numpy as np

# [3,4] 크기의 배열을 생성하여 0으로 채움

print(np.zeros((3,4)))


"""
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
"""

- 넘파이로 배열을 생성하여 1로 채움

# [2,3,4] 크기의 배열을 생성하여 1로 채움
print(np.ones((2,3,4),dtype=np.int16))

"""
[[[1 1 1 1]
  [1 1 1 1]
  [1 1 1 1]]

 [[1 1 1 1]
  [1 1 1 1]
  [1 1 1 1]]]

다차원 배열이 가능해서 두묶음이 나옴   
"""

- 넘파이로 특정 크기의 배열을 생성

# 초기화 되지 않은 [2,3] 크기의 배열을 생성
print(np.empty((2,3)))

"""
[[1.39069238e-309 1.39069238e-309 1.39069238e-309]
 [1.39069238e-309 1.39069238e-309 1.39069238e-309]]
"""

- 넘파이로 단위 행렬 호출

# 단위 행렬, 고등학교 수학

import numpy as np

a1 = np.identity(n=3,dtype=np.int8)
print(a1)

'''
[[1 0 0]
 [0 1 0]
 [0 0 1]]

 단위 행렬
'''

'''
넘파이에는 행렬계산이 많음

'''

- 넘파이로 행렬간 덧셈과 뺄셈

import numpy as np

A = np.array([[1,2,3], [4,5,6]])
B = np.array([[7,8,9], [10,11,12]])

C = A+B
D = A-B

print("C =", C, "\nD=", D)

- 넘파이로 행렬간 곱셈

import numpy as np

# 2차원 X 2차원
a = np.array([[1,3], [2,4]])
b = np.array([[1,6], [3,0]])
c = np.dot(a,b)
print(c)

"""
[[10  6]
 [14 12]]
"""

- 행렬 반대로 출력


# 행렬을 반대로 출력

import numpy as np
b = np.arange(15)

a = np.arange(15).reshape(3,5)
# 형태를 바꾸어 3행 5열로 만들기

c = np.transpose(a)
# a의 행렬을 바꾼다, 행이었던 것이 열로 바뀜

print(b)
print(a)
print(c)

"""
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14]\
14까지 1차원으로 노출 

형태를 바꾸어 35열로 만들기
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]]

행렬을 바꿔서 노출시키기 
[[ 0  5 10]
 [ 1  6 11]
 [ 2  7 12]
 [ 3  8 13]
 [ 4  9 14]]

- np.arange()와 np.linspace()를 통한 연속적인 데이터 생성

# 어레인지에 대해 살펴보기
import numpy as np

# 10이상 30미만까지 5씩 차이나게 생성
print(np.arange(10,30,5))
# [10 15 20 25]

# 이상 2미만 까지 0.3 씩 차이나게 생성
print(np.arange(0, 2,0.3))
# [0.  0.3 0.6 0.9 1.2 1.5 1.8]
# 실수도 가능

# --------- 몇등분 하세요 계산도 가능
# 0~ 99까지 100등분

x = np.linspace(0,99,100)
print(x)

"""
np.arange()와 np.linspace()를 이용하여 연속적인 데이터도 쉽게 생성할 수 있음.

np.arange() : n 만큼 차이나는 숫자 생성
np.linspace() : n 등분한 숫자 생성
"""

- 넘파이를 통한 행렬 데이터 변경


import numpy as np

a = np.arange(6)
print(a)
# [ 0 1 2 3 4 5]


b = np.arange(12).reshape(4,3)
# 0부터 11까지 1차원으로 나오는걸 4행 3열로 바꿔라
print(b)
# [[ 0  1  2]
# [ 3  4  5]
# [ 6  7  8]
# [ 9 10 11]]


c = np.arange(24).reshape(2,3,4)
# 0부터 23까지, 두개 묶음으로 3행 4열로 만들어라 
print(c)

# [[[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

# [[12 13 14 15]
#  [16 17 18 19]
#  [20 21 22 23]]]


print(np.arange(10000).reshape(100,100))
# [10000] 배열을 100행 100열로 바꾸어라

#[[   0    1    2 ...   97   98   99]
# [ 100  101  102 ...  197  198  199]
# [ 200  201  202 ...  297  298  299]
# ...
# [9700 9701 9702 ... 9797 9798 9799]
# [9800 9801 9802 ... 9897 9898 9899]
# [9900 9901 9902 ... 9997 9998 9999]]

- 넘파이에 함수를 통한 결과 노출


# 함수를 넣어보기

import numpy as np

a1=np.arange(8)
print(a1)
# [0 1 2 3 4 5 6 7]

a = np.arange(8).reshape(2,4)**2  #2행 4열 만든다음에 거듭제곱
print(a)
#[[ 0  1  4  9]
# [16 25 36 49]]

# 모든 요소의 합구하는 함수
print(a.sum())
# 140

# 모든 요소 중 최대값
print(a.max())
# 49

# 모든 요소 중 최대값의 인덱스
print(a.argmax())
# 7

# 모든 요소의 누적 합
print(a.cumsum())

- 넘파이 연산


import numpy as np

a = np.array( [20,30,40,50])
b = np.array(4)  #0 1 2 3
print(b)

# a에서 b의 각각의 원소를 - 연산
c = a-b
print(c)
# [20 29 38 47]

#b 각각의 원소에 제곱 계산
print(b**2)
# [0 1 4 9]

# a 각각의 원소가 35보다 작은지 Boolean 결과
print(a<35)
# [ True  True False False]

# -----
# 행렬 계산
"""
* :  각각의 워소끼리 곱셈
@ : 행렬 곱셈 (Matrix product)
"""

A = np.array([[1,1],
              [0,1]])
B = np.array([[2,0],
              [3,4]])
print(A*B)
# [[2 0]
# [0 4]]
# 행렬의 연산은 아님, 걍 더한겨?

print(A@B)
#[[5 4]
# [3 4]]

- 넘파이 제곱계산


import numpy as np

# 제곱계산
a = np.arange(10)**3
print(a)
# [  0   1   8  27  64 125 216 343 512 729]
print(a[2])
# 8

# 2~4번 인덱스
print(a[2:5])
# [8 27 64]

- 브로드캐스팅

# 브로드캐스팅

import numpy as np
arr1 = np.array([[0,0,0,],
                 [1,1,1],
                 [2,2,2]])
arr2 = np.array([5,6,7,])

print(arr1 + arr2)

"""

[[5 6 7]
 [6 7 8]
 [7 8 9]]

브로드캐스팅(Broadcasting) : 일정 조건을 부합하는 다른 형태의 배열끼리 연산을 수행하는 것을 의미

[브로드캐스팅 조건]
1. 멤버가 하나일 배열은 어떤 배열에나 브로드캐스팅(Broadcasting)이 가능, (단 멤버가 하나도 없는 빈 배열을 제외)
ex) 4x7+1

2. 하나의 배열의 차원이 1인 경우 브로드캐스팅(Broadcasting)이 가능
ex) 4x4 + 1x4

3. 차원의 짝이 맞을 때 브로드캐스팅(Broadcasting) 가능
ex) 3x1 + 1x3

"""

value = np.array([[1,2,3,4],[2,5,6,7],[8,9,10,11],[12,13,14,15]])
value1 = np.array([1])
print(value + value1) # 4x4 +1

value2 = np.array([3,3,3,3])
print(value+value2) #4x4 + 1x4
profile
비전공자 QA의 자기개발 공부노트

0개의 댓글