Python Camp

Logo

Life is short. You need Python.

1. Numpy 란?

Numpy 는 C언어로 구현된 파이썬 라이브러리로써, 고성능의 수치계산을 위해 제장되었습니다.

Numpy는 Numerical Python의 줄임말이기도 한 Numpy는 벡터 및 행렬 연산에 있어서 매우 편리한 기능을 제공합니다.

numpy에서는 기본적으로 array라는 단위로 데이터를 관리하며 이에 대해 연산을 수행합니다.

관례상 Scipy와 PyData를 사용하는 대부분의 사용자는 Numpy를 별칭(alias)인 np를 사용해 import 한다.

import numpy as np

import는 파이썬에서 라이브러리를 읽기 위해 사용하는 명령어이다.

Numpy는 외부라이브러리로 본래 파이썬을 다운받을 때 존재하지 않는 라이브러리이다. 그래서 이 모듈을 가져오기 위해서 import를 사용한다.

## ndarray 다차원 배열 객체


ndarray는 같은 종류의 데이터를 담을 수 있는 포괄적인 다차원 배열이다. ndarray의 모든 원소는 같은 자료형이어야만 한다.

import numpy as np

a = np.array([1, 2, 3])  # rank가 1인 배열 생성
a.dtype
dtype('int64')
a.shape  
(3,)
b = np.array([[1,2,3],[4,5,6]])
type(b)
numpy.ndarray
b.shape
(2, 3)

## 1. 1 배열 생성 함수 ###

np.array()

다차원 배열 생성시 배열의 길이가 동일해야 함.

다차원 배열은 일종의 매트릭스와도 비슷하다.

순서가 있는 배열은 벡터(;크기와 방향을 가진 양; 수와 순서쌍으로 구성)와도 비슷해보임

np.zeros , np.ones

np.zeros((3,4))
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])

np.empty

np.arange(n)

2. Numpy 배열의 기초 (배열 슬라이싱 : 하위 배열에 접근하기)

1차원 배열

x = np.arange(10)
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
x[:5]
array([0, 1, 2, 3, 4])
x[5:]
array([5, 6, 7, 8, 9])
x[4:7]
array([4, 5, 6])
x[::2]
array([0, 2, 4, 6, 8])
x[1::2]
array([1, 3, 5, 7, 9])
x[::-1]
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
x[5::-2]
array([5, 3, 1])

실습 문제

a=np.arange(13)
a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])

답변 ↓

print(a[-3::-3])
[10  7  4  1]

3. Numpy 배열의 기초 (배열 인덱싱 : 단일 요소에 접근하기)

x1 = np.random.randint(10, size=((3,4)))
x1
array([[9, 3, 4, 9],
       [6, 6, 7, 8],
       [6, 2, 1, 5]])
x1[0,0]
9
x1[0,0] =12
x1
array([[12,  3,  4,  9],
       [ 6,  6,  7,  8],
       [ 6,  2,  1,  5]])

다차원 배열에서는 콤마로 구분된 인덱스 튜플을 이용해 배열 항목에 접근할 수 있습니다.

인덱스 표기법을 사용해 값을 수정할 수도 있습니다.

x2 = np.random.randint(10,size=(3,4))
x2
array([[6, 4, 4, 3],
       [0, 0, 8, 8],
       [1, 4, 0, 2]])
x2[:2,:3]
array([[6, 4, 4],
       [0, 0, 8]])

4. 배열의 재구조화

grid = np.arange(1,10).reshape((3,3))
grid
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
grid.reshape((1,9))
array([[1, 2, 3, 4, 5, 6, 7, 8, 9]])
grid.reshape((3,2))
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-24-df39b94ccfc8> in <module>
----> 1 grid.reshape((3,2))


ValueError: cannot reshape array of size 9 into shape (3,2)

## 배열 연결 및 분할

### 배열 연결

x = np.array([1,2,3])
y = np.array([3,2,1])
np.concatenate([x,y])
array([1, 2, 3, 3, 2, 1])

한번에 두 개 이상의 배열을 연결할 수도 있다.

z=[99,99,99]
print(np.concatenate([x,y,z]))
[ 1  2  3  3  2  1 99 99 99]

### np.concatenate는 2차원 배열에서도 사용할 수 있다.

grid = np.array([[1, 2, 3],[4, 5, 6]])
np.concatenate([grid, grid])
array([[1, 2, 3],
       [4, 5, 6],
       [1, 2, 3],
       [4, 5, 6]])
np.concatenate([grid, grid], axis=1)
array([[1, 2, 3, 1, 2, 3],
       [4, 5, 6, 4, 5, 6]])

5. Numpy 배열 연산 : 유니버설 함수

cf) 인터프리터 언어란?

cf) 컴파일 언어란?

np.arange(5)/np.arange(1,6)
array([0.        , 0.5       , 0.66666667, 0.75      , 0.8       ])

#### 배열 산술 연산- NumPy ufuncs는 파이썬의 기본 산술 연산자를 사용한다.

x= np.arange(4)
print("x=",x)
print("x+5=",x+5)
print("x-5=",x-5)
print("x*2=",x*2)
print("x/2=",x/2)
print("x//2=",x//2) # 바닥 나눗셈( 나머지는 버림)
x= [0 1 2 3]
x+5= [5 6 7 8]
x-5= [-5 -4 -3 -2]
x*2= [0 2 4 6]
x/2= [0.  0.5 1.  1.5]
x//2= [0 0 1 1]

산술 연산은 사용상 편의를 위해 Numpy에 내장된 특정 함수로 감싼 것이다.

np.add(x,5)
array([5, 6, 7, 8])

절댓값 함수에 대응하는 NumPy ufunc는 np.absolute로, np.abs라는 별칭으로도 사용가능하다.

복소수 데이터도 처리할 수 있으며, 이경우에는 절댓값은 크기를 반환한다.

x=np.array([-2,-1,0,1,2])
np.abs(x)
array([2, 1, 0, 1, 2])
np.absolute(x)
array([2, 1, 0, 1, 2])
x=np.array([3-4j, 4-3j, 2+0j, 0+1j])
np.abs(x)
array([5., 5., 2., 1.])

삼각함수 - 데이터에서 가장 유용한 함수 중 일부는 삼각함수이다.

theta = np.linspace( 0, np.pi, 3)

print("theta = ", theta)
print(" sin(theta)= ", np.sin(theta))
print(" cos(theta)= ", np.cos(theta))
print(" tan(theta)= ", np.tan(theta))

theta =  [0.         1.57079633 3.14159265]
 sin(theta)=  [0.0000000e+00 1.0000000e+00 1.2246468e-16]
 cos(theta)=  [ 1.000000e+00  6.123234e-17 -1.000000e+00]
 tan(theta)=  [ 0.00000000e+00  1.63312394e+16 -1.22464680e-16]

지수와 로그

x=[1,2,3]
print("x = ",x)
print("e^x = ", np.exp(x) )
print("2^x =", np.exp2(x))
print("3^x =", np.power(3, x))
x =  [1, 2, 3]
e^x =  [ 2.71828183  7.3890561  20.08553692]
2^x = [2. 4. 8.]
3^x = [ 3  9 27]
x = [1, 2, 4, 10]
print("x =", x)
print("ln(x) =", np.log(x))
print("log2(x) =", np.log2(x))
print("log10(x) =", np.log10(x))
x = [1, 2, 4, 10]
ln(x) = [0.         0.69314718 1.38629436 2.30258509]
log2(x) = [0.         1.         2.         3.32192809]
log10(x) = [0.         0.30103    0.60205999 1.        ]

출력지정

x = np.arange(5)
y = np.empty(5)
np.multiply(x, 10, out=y)
print(y)
[ 0. 10. 20. 30. 40.]
y = np.zeros(10)
np.multiply(2, x, out=y[::2])
print(y)
[0. 0. 2. 0. 4. 0. 6. 0. 8. 0.]

집계

x = np.arange(1, 6)
np.add.reduce(x)
15
np.multiply.reduce(x)
120
x = np.arange(1, 6)
np.add.accumulate(x)
array([ 1,  3,  6, 10, 15])
np.multiply.accumulate(x)
array([  1,   2,   6,  24, 120])

6. 브로드 캐스팅

느린 파이썬 루프를 제거하기 위해 연산을 벡터화 하는 NumPy의 유니버설 함수 사용법을 했었다.

벡터화 연산의 또 다른 방법은 NumPy의 브로드 캐스팅 기능을 사용하는 것이다.

브로드캐스팅은 단지 다른 크기의 배열에 유니버설 함수(덧셈, 뺄셈, 곱셈 등)를 적용하기 위한 규칙의 집합이다.

a = np.array([1,2,3])
b=np.array([1,2])

a+b
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-45-cc3620bbd4ac> in <module>
      2 b=np.array([1,2])
      3 
----> 4 a+b


ValueError: operands could not be broadcast together with shapes (3,) (2,) 

브로드캐스팅은 어떤 조건만 만족한다면 모양이 다른 배열끼리의 연산도 가능하게 해주며 모양이 부족한 부분은 확장하여 연산을 수행할 수 있도록 한다.

확장 또는 전파한다는 의미로 Broadcasting을 설명하는 가장 간단한 예는 배열과 스칼라 값을 계산하는 것이다.

위의 그림에서는 첫 번째 그림에 해당하는 것으로 0, 1, 2라는 NumPy로 생성한 배열에 스칼라 5를 합한 결과가 5, 6, 7이라는 것을 알 수 있다. 일반적인 파이썬 리스트를 사용하면 for문을 이용해야 같은 결과를 얻을 수 있지만, NumPy에서는 브로드캐스팅의 개념 덕분에 5가 0이외에 1과 2의 원소 부분에도 전파(broadcast)되어 계산되어 간단하게 합 연산을 수행하는 것만으로 같은 결과를 얻을 수 있었다.

두 번째 그림은 배열 간의 계산으로 배열의 차원이 확대된 케이스이다. 두 번째 그림은 3x3 배열에 1x3 배열을 합 연산한 경우이다. 각 행에 동일한 계산을 전파한 것을 볼 수 있다.

세번째 그림은 브로드캐스팅의 확장성 측면을 극명하게 보여주는 케이스이다. 3x1 배열과 1x3 배열의 합을 했는데 두 번째에서는 한쪽의 더 낮은 차원의 배열에서만 아래(0번 축) 방향으로 broadcast한 것에 반해 양 쪽 배열에서 broadcast한 것을 확인할 수 있다.

브로드캐스팅이 일어날 수 있는 조건은 다음과 같다.

  • 규칙 1 : 두 배열의 차원 수가 다른 경우 치수가 더 작은 배열의 모양에 선행 (왼쪽)면이있는 모양이 채워집니다.



  • 규칙 2 : 두 배열의 모양이 임의의 차원에서 일치하지 않으면 해당 차원의 모양이 1 인 배열이 다른 모양과 일치하도록 늘어납니다.



  • 규칙 3 : 크기가 어느 정도라도 크기가 일치하지 않고 둘 다 1과 같으면 오류가 발생합니다.
a = np.array([1,2,3])
b=np.array([1,2])

a+b
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-46-cc3620bbd4ac> in <module>
      2 b=np.array([1,2])
      3 
----> 4 a+b


ValueError: operands could not be broadcast together with shapes (3,) (2,) 
b.shape=(2,1)

a+b
array([[2, 3, 4],
       [3, 4, 5]])
np.ones((3,1))
array([[1.],
       [1.],
       [1.]])
np.arange(3)
array([0, 1, 2])
np.ones((3,3))+np.arange(3)
array([[1., 2., 3.],
       [1., 2., 3.],
       [1., 2., 3.]])

비교, 마스크, 부울 로직

Numpy는 요소단위의 유니버셜 함수로 비교연산자도 구현한다.

rng = np.random.RandomState(0)
x = rng.randint(10, size=(3, 4))
x
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 6]])
x < 6
array([[ True,  True,  True,  True],
       [False, False,  True,  True],
       [ True,  True, False, False]])
# 8보다 큰 값이 하나라도 있는가
np.any(x > 8)
True
#0보다 작은 값이 하나라도 있는가?
np.any(x < 0)
False
#모든 값이 10보다 작은가
np.all(x<10)
True
#모든 값이 6과 같은가?
np.all(x==6)
False

값 중 하나라도 참이 있는지나 모든 값이 참인지 빠르게 확인하고 싶을때 np.any() 나 np.all()을 사용하면 된다.

# 각 행의 모든 값이 8보다 작은가
np.all(x < 8, axis=1)
array([ True, False,  True])

axis =0 세로 열,

axis =1 가로 행

마스크로서의 부울 배열

x
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 6]])
x < 5
array([[False,  True,  True,  True],
       [False, False,  True, False],
       [ True,  True, False, False]])
x[x < 5]
array([0, 3, 3, 3, 2, 4])

반환된 값은 이 조건에 맞는 모든 값, 마스크 배열이 True인 위치에 있는 모든 값으로 채워져서 출력이 됩니다.

팬시인덱싱

팬시인덱싱은 정수 리스트를 이용해서 여러 개를 동시에 선택하는 방식입니다.

import numpy as np
rand = np.random.RandomState(42)
x = rand.randint(100, size=10)
print(x)
[51 92 14 71 60 20 82 86 74 74]
[x[3], x[7], x[2]]
[71, 86, 14]

세 개의 다른 요소에 접근하고자 할 때, 다음과 같이 할 수 있습니다.

ind = [3, 7, 4]
x[ind]
array([71, 86, 60])

인덱스의 단일 리스트나 배열을 전달해서 팬시인덱싱을 할 수도 있습니다.

X = np.arange(12)
X
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
i =  np.array([2,4,6,8,10])
X[i]=0
print(X)
[ 0  1  0  3  0  5  0  7  0  9  0 11]

Pandas

Pandas는 파이썬에서 사용하는 데이터 분석, 데이터 처리 등을 쉽게 하기 위해 만들어진 라이브 러리 입니다.

1. Pandas 자료구조

Pandas는 Series, DataFrame 두 자료 구조가 가장 중요합니다.

1-1 Series

1차원 배열 같은 구조입니다.

import pandas as pd

Series 정의하기

obj = pd.Series([4,1,2,3])
obj
0    4
1    1
2    2
3    3
dtype: int64

Series의 값만 확인하기

obj.values
array([4, 1, 2, 3])

Series의 인덱스만 확인하기

obj.index
RangeIndex(start=0, stop=4, step=1)

Series의 자료형 확인하기

obj.dtypes
dtype('int64')

인덱스를 변경할수도 있습니다.

obj2=pd.Series([20,21,25,4],index=['a','b','c','d'])
obj2
a    20
b    21
c    25
d     4
dtype: int64

dictionary 자료형을 Series data로 만들 수 있습니다.

dictionary의 key가 Series의 index가 됩니다.

data = {'Hong':27,'Yang':25,'Kim':27}
obj3=pd.Series(data)
obj3
Hong    27
Yang    25
Kim     27
dtype: int64

자료들의 이름을 지정해 줄수도 있다.

obj3.name = 'Lab'
obj3
Hong    27
Yang    25
Kim     27
Name: Lab, dtype: int64
obj3.index.name = "NAMES"
obj3
NAMES
Hong    27
Yang    25
Kim     27
Name: Lab, dtype: int64

Series를 사용하는 이유는 values 와 index 속성으로 접근할 수 있습니다.

obj3[:'Yang']
NAMES
Hong    27
Yang    25
Name: Lab, dtype: int64
obj3[0:3]
NAMES
Hong    27
Yang    25
Kim     27
Name: Lab, dtype: int64
population_dict = {'California': 38332521,'Texas': 26448193,'New York': 19651127,'Florida': 19552860,'Illinois': 12882135}

population = pd.Series(population_dict)
population
California    38332521
Texas         26448193
New York      19651127
Florida       19552860
Illinois      12882135
dtype: int64

원하는 인덱스만 지정해서 값을 출력할 수 도 있습니다.

 pd.Series(population_dict,index=['Texas','Florida'])
Texas      26448193
Florida    19552860
dtype: int64

Pandas DataFrame 객체

Series가 유연한 인덱스를 가지는 1차원 배열이라면

DataFrame은 유연한 행 인덱스와 유연한 열 이름을 가진 2차원 배열이라고 볼수 있습니다.

population_dict = {'California': 38332521,'Texas': 26448193,'New York': 19651127,'Florida': 19552860,'Illinois': 12882135}

area_dict = {'California': 423967, 'Texas': 695662, 'New York': 141297,'Florida': 170312, 'Illinois': 149995}
area =pd.Series(area_dict)
area
California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
dtype: int64
states = pd.DataFrame({'population': population,'area': area})
states
population area
California 38332521 423967
Texas 26448193 695662
New York 19651127 141297
Florida 19552860 170312
Illinois 12882135 149995
states.index
Index(['California', 'Texas', 'New York', 'Florida', 'Illinois'], dtype='object')
states.columns
Index(['population', 'area'], dtype='object')

DataFrame은 열 이름을 열데이터로 이뤄진 Series로 매핑합니다.

states['area']
California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
Name: area, dtype: int64

위에서 보는거와 같이 열 이름을 넣었을 때 DataFrame형식이 아닌 Series 형태로 나오는 것을 확인할 수 있습니다.

pd.DataFrame(population,columns=['area'])
area
California 38332521
Texas 26448193
New York 19651127
Florida 19552860
Illinois 12882135

Series와 마찬가지로 DataFrame의 index와 columns에 이름을 붙일 수 있습니다

data={"name":["용기","재은","태진","재열"],"Year":[1993,1995,1992,1993],"number":[1,2,3,4]}
df=pd.DataFrame(data)
df
name Year number
0 용기 1993 1
1 재은 1995 2
2 태진 1992 3
3 재열 1993 4
df.index.name ="Num"
df.columns.name = "Info"
df
Info name Year number
Num
0 용기 1993 1
1 재은 1995 2
2 태진 1992 3
3 재열 1993 4

딕셔너리에서 정의한 키값을 인자로 columns에 직접 넣어 columns의 순서를 새롭게 정의할 수 있으며, 키값이 미리 존재하지 않는 이름을 columns 인자의 성분으로 넣는다면, 그 columns 은 NaN으로 표시됩니다.

(NaN은 Not a Number를 의미하며, 해당하는 값이 없다는 뜻입니다.)

df2=pd.DataFrame(data,columns=["Year","name","number","project"])
df2
Year name number project
0 1993 용기 1 NaN
1 1995 재은 2 NaN
2 1992 태진 3 NaN
3 1993 재열 4 NaN

DataFrame에서 열을 선택(인덱싱)하여 조작하기

하나의 열을 가져오는 방법은 2가지이다.

DataFrame 변수[“칼럼명”]

  1. df[“컬럼이름”]

  2. df.컬럼이름

DataFrame에서 하나의 열을 가져온 결과는 Series의 모양을 하고 있다

df2["name"]
0    용기
1    재은
2    태진
3    재열
Name: name, dtype: object
df2.name
0    용기
1    재은
2    태진
3    재열
Name: name, dtype: object

2개 이상의 열을 가져오는 방법은 df[[“컬럼이름1”,”컬럼이름2”]]의 방법으로 가져온다.

df2[["Year","name"]]
Year name
0 1993 용기
1 1995 재은
2 1992 태진
3 1993 재열
df2
Year name number project
0 1993 용기 1 NaN
1 1995 재은 2 NaN
2 1992 태진 3 NaN
3 1993 재열 4 NaN

선택된 열의 모든 성분에 값을 대입할 수도 있습니다.

df2["project"]="Ai"
df2
Year name number project
0 1993 용기 1 Ai
1 1995 재은 2 Ai
2 1992 태진 3 Ai
3 1993 재열 4 Ai
df2["project"]=["Ai","Ai","Ai","PET"]
df2
Year name number project
0 1993 용기 1 Ai
1 1995 재은 2 Ai
2 1992 태진 3 Ai
3 1993 재열 4 PET

새로운 열을 생성과 동시에 대입할 수도 있습니다.

df2["grade"]=["4학년","대학원생","대학원생","대학원생"]
df2
Year name number project grade
0 1993 용기 1 Ai 4학년
1 1995 재은 2 Ai 대학원생
2 1992 태진 3 Ai 대학원생
3 1993 재열 4 PET 대학원생

DataFrame의 하나의 열을 선택했을 때 Series로 제공된다는 점에서 착안하여, 새로운 열을 입력할 때도, 리스트가 아닌 Series를 대입해 줄 수도 있습니다.

add=pd.Series([1,2,3,4],index=[3,2,1,0])
df2["등수"]=add
df2
Year name number project grade 등수
0 1993 용기 1 Ai 4학년 4
1 1995 재은 2 Ai 대학원생 3
2 1992 태진 3 Ai 대학원생 2
3 1993 재열 4 PET 대학원생 1

Series를 만들어서 대입하는 이유는, Series를 정의할 때, 기존의 행 index와 동일한 이름으로 주면, 맞춰서 끼어들어가는 특징을 이용할 수 있기 때문입니다.

행과 열 바꾸기

df2.T
0 1 2 3
Year 1993 1995 1992 1993
name 용기 재은 태진 재열
number 1 2 3 4
project Ai Ai Ai PET
grade 4학년 대학원생 대학원생 대학원생
등수 4 3 2 1
score ={"수학":[30,90],"영어":[60,100]}
df3= pd.DataFrame(score,index=["철수","영희"])
df3
수학 영어
철수 30 60
영희 90 100
df3.T
철수 영희
수학 30 90
영어 60 100

To Home
To Lecture List