Python for data analysis

Chapter 4. Numpy 기본 : 배열과 벡터 계산

표독's 2016. 3. 11. 17:45

Chapter 4. Numpy : 배열과 벡터 계산


Numerical Python의 줄임말인 Numpy는 고성능의 과학계산 컴퓨팅과 데이터 분석에 필요한 기본 패키지다. Numpy는 이 책에서 사용하는 거의 모든 종류의 고수준의 도구를 작성하는 데 토대가 되는 패키지로 제공하는 기능은 다음과 같다.


-빠르고 메모리를 효율적으로 사용하며 벡터 산술연산과 세련된 브로드캐스팅 기능을 제공하는 다차원 배열인 ndarray

-반복문을 작성할 필요 없이 전체 데이터 배열에 대해 빠른 연산을 제공하는 표준 수학 함수

-배열 데이터를 디스크에 쓰거나 읽을 수 있는 도구와 메모리에 올려진 파일을 사용하는 도구

-선형대수, 난수 발생기, 푸리에 변환 기능

-C, C++, 포트란으로 쓰여진 코드를 통합하는 도구


마지막 항목은 생태계 관점에서 봤을 때 가장 중요한 기능으로 Numpy는 사용하기 편한 C API를 제공하며 데이터를 다른 저수준 언어로 쓰여진 외부 라이브러리에 쉽게 전달 할 수 있다.

또한 외부 라이브러리에서 반환된 데이터를 파이썬의 NumPy 배열 형태로 불러올 수도 있다. 


NumPy 그 자체로는 고수준의 데이터 분석 기능을 제공하지 않으므로 NumPy 배열과 배열 기반의 컴퓨팅에 대한 이해를 한다면 pandas 같은 도구를 좀 더 효율적으로 사용할 수 있다. 


대부분 데이터 분석 애플리케이션에서 중요하게 사용되는 기능

- 벡터 배열상에서 데이터 개조, 정제, 부분 집합, 필터링, 변형, 다른 종류 연산의 빠른 수행 

- 정렬, 유일 원소 찾기, 집합연산 같은 일반적인 배열 처리 알고리즘

- 통계의 효과적인 표현과 데이터의 수집/요약

- 다른 종류의 데이터 묶음을 병합하고 엮기 위한 데이터 정렬과 데이터 간의 관계 조작

- if - elif - else를 포함하는 반복문 대신 사용할 수 있는 조건절을 표현할 수 있는 배열 표현            (comprehension)

- 데이터 그룹 전체에 적용할 수 있는 수집, 변형, 함수 적용 같은 데이터 처리.


import numpy as np

NumPy는 이런 연산을 위한 기본 라이브러리를 제공한다.


4.1 NumPy ndarray: 다차원 배열 객체


NumPy의 핵심 기능 중 하나는 N차원의 배열 객체 또는 ndarray로 파이썬에서 사용할 수 있는 대규모 데이터 집합을 담을 수 있는 빠르고 유연한 자료 구조다.

In[2]: import numpy as np
In[3]: data = np.random.randn(2, 3)
In[4]: data
Out[4]:
array([[-0.56285369, -0.06812205, 0.75793005],
[ 0.71350143, 0.114842 , -0.78867801]])
In[6]: data * 10
Out[6]:
array([[-5.62853693, -0.68122054, 7.5793005 ],
[ 7.13501431, 1.14842001, -7.88678008]])
In[7]: data + data
Out[7]:
array([[-1.12570739, -0.13624411, 1.5158601 ],
[ 1.42700286, 0.229684 , -1.57735602]])

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


모든 배열은 각 차원의 크기를 알려주는 shape라는 튜플과 배열에 저장된 자료형을 알려주는 dtype라는 객체를 가지고 있다.


In[8]: data.shape
Out[8]: (2L, 3L)
In[9]: data.dtype
Out[9]: dtype('float64')

이 장에서는 NumPy의 배열을 사용하는 기초 방법에 대해 소개한다.


배열 위주 프로그래밍과 생각하는 방법에 능숙해지는 것이 파이썬을 이용한 과학계산의 고수가 되는 지름길이다.


4.1.1 ndarray 생성


배열을 생성하는 가장 쉬운 방법은 array함수를 이용하는 것이다. 순차적인 객체(다른 배열도 포함하여)를 받아 넘겨받은 데이터가 들어잇는 새로운 NumPy 배열을 생성한다. 예를 들어 파이썬의 리스트는 변환하기 좋은 예다.


In[10]: data1 = [6, 7.5, 8, 0, 1]
In[11]: arr1 = np.array(data1)
In[12]: arr1
Out[12]: array([ 6. , 7.5, 8. , 0. , 1. ])

같은 길이의 리스트가 담겨있는 순차 데이터는 다차원 배열로 변환이 가능하다.


In[23]: data2 = [[1,2,3,4],[5,6,7,8]]
In[24]: arr2 = np.array(data2)
In[25]: arr2
Out[25]:
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
In[26]: arr2.ndim
Out[26]: 2
In[27]: arr2.shape
Out[27]: (2L, 4L)


명시적으로 지정하지 않는 한 np.array는 생성될 때 적절한 자료형을 추정한다. 그렇게 추정된 자료형은 dtype 객체에 저장되는데, 먼저 살펴본 예로 들어 보면,

In[28]: arr1.dtype
Out[28]: dtype('float64')
In[29]: arr2.dtype
Out[29]: dtype('int32')

또한 np.array는 새로운 배열을 생성하기 위한 여러 함수를 가지고 있는데,

 예를 들면 zeros와 ones는 주어진 길이나 모양에 각각 0과 1이 들어있는 배열을 생성한다.

 empty함수는 초기화되지 않은 배열을 생성하는데,

이런 메서드를 사용해서 다차원 배열을 생성하려면 원하는 형태의 튜플을 넘기면 된다.


In[30]: np.zeros(10)
Out[30]: array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
In[31]: np.zeros((3, 6)) # 6개의 0으로 구성된 리스트 3개
Out[31]:
array([[ 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0.]])
In[32]: np.empty((2, 3, 2)) '''2개의 초기화되지 않은 값으로 채워진 배열을 가진 리스트

3개를 가진 리스트를 2개 생성 '''
Out[32]:
array([[[ 8.74496193e-322, 0.00000000e+000],
[ 0.00000000e+000, 0.00000000e+000],
[ 0.00000000e+000, 0.00000000e+000]],

[[ 0.00000000e+000, 0.00000000e+000],
[ 0.00000000e+000, 0.00000000e+000],
[ 0.00000000e+000, 0.00000000e+000]]])

arange는 파이썬의 range 함수의 배열 버전이다.

In[33]: np.arange(15)
Out[33]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])

배열 생성 함수 목록은 page 119 표 4-1 참조


4.1.2 ndarray의 자료형


자료형, dtype은 ndarray가 특정 데이터를 메모리에서 해석하기 위해 필요한 정보를 담고 있는 특수한 객체다.

In[34]: arr1 = np.array([1, 2, 3], dtype=np.float64)
In[35]: arr2 = np.array([1, 2, 3], dtype=np.int32)
In[36]: arr1.dtype
Out[36]: dtype('float64')
In[37]: arr2.dtype
Out[37]: dtype('int32')

dtype가 있기에 NumPy가 강력하면서도 유연한 도구가 될 수 있었는데, 대부분의 데이터는 디스크에서 데이터를 읽고 쓰기 편하도록 하위 레벨의 표현에 직접적으로 맞춰져 있으며, C나 포트란 같은 저수준 언어로 작성된 코드와 쉽게 연동이 가능하다. 산술 데이터의 dtype는 float, int 같은 자료형의 이름과 하나의 원소가 차지하는 비트 수로 이루어진다. 파이썬의 float 객체에서 사용되는 표준 배정밀도 부동소수점 값은 8바이트 혹은 64비트로 이루어지는데, 이 자료형은 NumPy에서 float64로 표현된다.


NumPy 자료형 목록은 page 120 표 4-2 참조


NOTE NumPy의 모든 dtype을 외울 필요는 없다. 주로 사용하게 될 자료형의 일바적인 종류만 신경 쓰면 된다. 주로 대용량 데이터가 메모리나 디스크에 저장되는 방식을 제어해야 할 필요가 있을 때 알아 두면 좋다.


ndarray의 astype 메서드를 사용해서 dtype을 다른 형으로 명시적 변경이 가능하다.


In[41]: arr = np.array([1, 2, 3, 4, 5])
In[42]: arr.dtype
Out[42]: dtype('int32')
In[43]: float_arr = arr.astype(np.float64) #int32에서 float64로 자료형 변경
In[44]: float_arr.dtype # 변경됨을 확인, 정수형을 부동소수점으로 변환하였다.
Out[44]: dtype('float64')

만약 부동소수점 숫자를 정수형으로 변환하면 소수점 아랫자리는 버려질 것이다.


In[45]: arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
In[46]: arr
Out[46]: array([ 3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
In[47]: arr.astype(np.int32)
Out[47]: array([ 3, -1, -2, 0, 12, 10]) # 소숫점 아랫자리가 버려졌다.

숫자 형식의 문자열을 담고 있는 배열이 있다면 astype을 사용하여 숫자로 변환 할 수 있다. 


In[50]: numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_) #문자열을 가지고 있는 배열
In[51]: numeric_strings.astype(float) #astype명령을 사용해 문자열을 float으로 바꾸어줌, float이라고해도 알아서 바꿔줌
Out[51]: array([ 1.25, -9.6 , 42. ])
int_array = np.arange(10)
calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
int_array.astype(calibers.dtype) #int_array배열의 자료형을 calibers 배열의 dtype인 float64형태로 맞추어줌 , 결과는 생략

4.1.3 배열과 스칼라 간의 연산


배열은 for 반복문을 작성하지 않고 데이터를 일괄처리할 수 있기 때문에 중요하다. 이를 벡터화라고 하는데, 같은 크기의 배열 간 산술연산은 배열의 각 요소 단위로 적용된다. (element wise **)

In[52]: arr = np.array([[1., 2., 3.], [4., 5., 6.]])
In[53]: arr
Out[53]:
array([[ 1., 2., 3.],
[ 4., 5., 6.]])
In[54]: arr* arr
Out[54]:
array([[ 1., 4., 9.],
[ 16., 25., 36.]])
In[55]: arr- arr
Out[55]:
array([[ 0., 0., 0.],
[ 0., 0., 0.]])

스칼라 값에 대한 산술연산은 각 요소로 전달된다.


크기가 다른 배열 간의 연산은 브로드캐스팅이라고 한다.

이에 대해서는 12장에서 하게 된다.


4.1.4 색인과 슬라이싱 기초

NumPy 배열 색인에 대해서는 다룰 주제가 많다. 데이터의 부분 집합이나 개별 요소를 선택 하기 위한 수많은 방법이 존재한다. 1차원 배열은 단순하며 표면적으로는 파이썬의 리스트와 유사하게 동작한다.


In[56]: arr = np.arange(10) 
In[57]: arr
Out[57]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In[58]: arr[5] #5번째 수 불러오기
Out[58]: 5
In[59]: arr[5:8] # 6번째에서부터 8번째까지
Out[59]: array([5, 6, 7])

In[60]: arr[5:8] = 12 # 6~8번째 수를 12로 치환 In[61]: arr Out[61]: array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])

이처럼 배열 조각에 스칼라 값을 대입하면 12가 선택 영역 전체로 전파된다. (이후로는 브로드캐스팅이라고 한다.) 리스트와의 중요한 차이점은 배열 조각은 원본 배열의 라는 점이다. 

즉, 데이터는 복사되지 않고 뷰에 대한 변경은 그대로 원본 배열에 반영된다는 것이다. 


In[62]: arr_slice = arr[5:8]
In[63]: arr_slice[1]
Out[63]: 12
In[64]: arr_slice[1] = 12345
In[65]: arr
Out[65]: array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8, 9])

# 여기서 보면 arr_slice의 변수에서 슬라이싱하고 조작했던게 arr에도 반영된다. 연동되어있다.

NumPy는 대용량 데이터 처리를 염두에 두고 설계되었기 때문에 만약 NumPy가 데이터 복사를 남발한다면 성능과 메모리 문제에 직면할 것이다. 


NOTE : 만약에 뷰 대신 ndrray 슬라이스의 복사본을 얻고 싶다면 arr[5:8].copy()를 사용해서 명시적으로 배열을 복사하면 된다.


다차원 배열을 다루려면 좀 더 많은 옵션이 필요하다. 2차원 배열에서 각 색인에 해당하는 요소는 스칼라 값이 아니라 1차원 배열이 된다.


In[66]: arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
In[67]: arr2d[2]
Out[67]: array([7, 8, 9])

따라서 개별 요소는 재귀적으로 접근해야 한다다음의 두 표현은 같다 하지만 그렇게 하기는 귀찮으니 분된 색인 리스트를 넘기면 된다

In[68]: arr2d[0][2]
Out[68]: 3
In[69]: arr2d[0, 2]
Out[69]: 3


In[73]: arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
In[74]: arr3d
Out[74]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],

[[ 7, 8, 9],
[10, 11, 12]]])
In[75]: arr3d[0]
Out[75]:
array([[1, 2, 3],
[4, 5, 6]])
In[76]: old_values = arr3d[0].copy()
In[77]: arr3d[0] = 42
In[78]: arr3d
Out[78]:
array([[[42, 42, 42],
[42, 42, 42]],

[[ 7, 8, 9],
[10, 11, 12]]])
In[79]: arr3d[0] = old_values
In[80]: arr3d
Out[80]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],

[[ 7, 8, 9],
[10, 11, 12]]])

슬라이스 색인


파이썬의 리스트 같은 1차원 객체처럼 ndarray는 익숙한 문법으로 슬라이싱할 수 있다.


In[87]: arr[1:6]
Out[87]: array([ 1, 2, 3, 4, 64])
In[88]: arr2d
Out[88]:
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
In[89]: arr2d[:2]
Out[89]:
array([[1, 2, 3],
[4, 5, 6]])
In[90]: arr2d[:2, 1:]
Out[90]:
array([[2, 3],
[5, 6]])
In[91]: arr2d[1, :2]
Out[91]: array([4, 5])
In[92]: arr2d[2, :1]
Out[92]: array([7])


물론 슬라이싱 구문에 값을 대입하면 선택 영역 전체에 값이 할당된다.


4.1.5 불리언 색인

중복된 이름이 포함된 배열이 있다고 하자. 그리고 numpy.random 모듈에 있는 randn 함수를 사용해서 임의의 표준정규분포 데이터를 생성하자.


In[93]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
In[94]: data = np.random.randn(7, 4) # 4개의 난수를 갖는 리스트를 7개 뱉어라
In[95]: names
Out[95]:
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'],
dtype='|S4')
In[96]: data
Out[96]:
array([[-0.5099588 , -0.26549335, 0.47190496, 1.28825708],
[-0.22645075, -0.33822525, 1.15948599, 0.62708074],
[-1.05545525, -0.29507054, 1.8157602 , -0.23001171],
[-0.2477215 , 0.16012906, 1.1783344 , 1.8553623 ],
[-0.82402224, 1.02722829, 0.54261483, -0.20464688],
[ 1.22017486, 1.25984082, 1.39068215, -0.61045326],
[ 1.40302308, -0.73592691, 1.28374355, 0.08466187]])

각각의 이름은 data 배열의 각 로우에 대응한다고 가정하자. 만약에 전체 로우에서 'Bob'과 같은 이름을 선택하려면 산술연산과 마찬가지로 배열에 대한 비교연산(== 같은) 도 벡터화 된다.


이 불리언 배열은 배열의 색인으로 사용할 수 있다.


이 불리언 배열은 반드시 색인하려는 축의 길이와 동일한 길이를 가져야 한다. 불리언 배열 색인도 슬라이스 또는 숫자 색인과 함께 혼용할 수 있다.



In[101]: names == 'Bob' # names에서 'Bob' 인 것들은 True 아니면 False
Out[101]: array([ True, False, False, True, False, False, False], dtype=bool)
In[102]: data[names == 'Bob'] # 0, 3에서 True
Out[102]:
array([[-0.5099588 , -0.26549335, 0.47190496, 1.28825708],
[-0.2477215 , 0.16012906, 1.1783344 , 1.8553623 ]])
In[103]: data[names == 'Bob', 2:] # 0,3 의 배열중에 3번째 부터 끝까지 뱉어라
Out[103]:
array([[ 0.47190496, 1.28825708],
[ 1.1783344 , 1.8553623 ]])

Q) 0,3 번째를 참조하는 값 어케?


'Bob'이 아닌 요소를 선택하려면 != 연산자를 사용하거나 -를 사용해서 조건절을 부정하면 된다.


name != 'Bob'

data[-(names == 'Bob')]


세 가지 이름 중에서 두 가지 이름을 선택하려면 &(and) 와 |(or) 같은 논리연산자를 사용한 여러 개의 불리언 조건을 조합하여 사용하면 된다.


In[107]: mask = (names == 'Bob') | (names == 'Will')


In[108]: mask #1,3,4,5만 True


In[107]: mask = (names == 'Bob') | (names == 'Will')
In[108]: mask #1,3,4,5만 True
Out[108]: array([ True, False, True, True, True, False, False], dtype=bool)
In[109]: data[mask] # 1,3,4,5 값만 내놔라
Out[109]:
array([[-0.5099588 , -0.26549335, 0.47190496, 1.28825708],
[-1.05545525, -0.29507054, 1.8157602 , -0.23001171],
[-0.2477215 , 0.16012906, 1.1783344 , 1.8553623 ],
[-0.82402224, 1.02722829, 0.54261483, -0.20464688]])

배열에 불리언 색인을 이용해서 데이터를 선택하면 반환되는 배열의 내용이 바뀌지 않더라도 항상 데이터 복사가 이루어진다.


불리언 배열에 값을 대입하는 것은 상식선에서 이루어지며, data에 저장된 모든 음수를 0으로 대입하려면 아래와 같이 하면 된다.

data[data <0] = 0

data[names != 'Joe'] = 7


4.1.6 팬시 색인

팬시 색인은 정수 배열을 사용한 색인을 설명하기 위해 NumPy에서 차용한 단어다. 8X4 크기의 배열이 있다고 하자.


In[120]: arr = np.empty((8, 4)) #초기화 되지 않은 4개의 값을 같는 리스트 8개로 된 배열만들기
In[121]: for i in range(8): # 0~7까지의 숫자를 i에 반복문, array 색인 마다 같은 번호 부여
... arr[i] = i
In[123]: arr
Out[121]:
array([[ 0., 0., 0., 0.],
[ 1., 1., 1., 1.],
[ 2., 2., 2., 2.],
[ 3., 3., 3., 3.],
[ 4., 4., 4., 4.],
[ 5., 5., 5., 5.],
[ 6., 6., 6., 6.],
[ 7., 7., 7., 7.]])

특정한 순서로 로우를 선택하고 싶다면 그냥 원하는 순서가 명시된 정수가 담긴 ndarray나 리스트를 넘기면 된다.


In[125]: arr[[4, 3, 0, 6]] # array중 5번째, 4번째, 1번째, 7번째 참조
Out[123]:
array([[ 4., 4., 4., 4.],
[ 3., 3., 3., 3.],
[ 0., 0., 0., 0.],
[ 6., 6., 6., 6.]])


다차원 색인 배열을 넘기는 것은 조금 다르게 동작하며, 각각의 색인 튜플에 대응하는 1차원 배열이 선택된다. 


In[131]: arr = np.arange(32).reshape((8,4)) # 4개의 값을 가진 리스트 8개를 반환하는데 0~31의 값을 갖게 한다.
In[132]: arr
Out[130]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23],
[24, 25, 26, 27],
[28, 29, 30, 31]])
In[133]: arr[[1, 5, 7, 2], [0, 3, 1, 2]]

'''2번째 배열에서 첫 번째 값, 6번째 배열에서 4번째 값, 8번째 베열에서 2번째 값, 3번째 배열에서 3번째 값'''

Out[131]: array([ 4, 23, 29, 10])

이 예제를 잠시 살펴보자 (1,0), (5,3), (7,1) (2,2)에 대응하는 요소가 선택 되었다. 이 예제에서 팬시 색인은 우리의 예상과는 조금 다르게 동작했다. 


행렬의 행과 열에 대응하는 사각형 모양의 값이 선택되기를 기대했는데 사실 그렇게 하려면 다음처럼 선택해야 한다.


In[135]: arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]] # 2, 6, 8, 3 행에서 1, 4, 2, 3순서로 담은 배열을 갖게 해라
Out[133]:
array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])

np.ix_ 함수를 사용하면 같은 결과를 얻을 수 있는데, 1차원 정수 배열 2개를 사각형 영역에서 사용할 색인으로 변환해준다.


In[138]: arr[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])]
Out[135]:
array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])

팬시 색인은 슬라이싱과는 달리 선택된 데이터를 새로운 배열로 복사한다.


4.1.7 배열 전치와 축 바꾸기

배열 전차는 데이터를 복사하지 않고 데이터 모양이 바귄 뷰를 반환하는 특별한 기능이다. ndarray는 transpose 메서드와 T라는 이름의 특수한 속성을 가지고 있다.


In[139]: arr = np.arange(15).reshape((3, 5))
In[140]: arr
Out[137]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
In[141]: arr.T
Out[138]:
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])

행렬 계산을 할 때 자주 사용하게 될 텐데, 예를 들면 행렬의 내적 

 는 np.dot을 이용해서 구할 수 있다.

In[143]: arr = np.random.randn(6, 3)
In[144]: np.dot(arr.T, arr)
Out[141]: # (3,6) X (6,3)
array([[ 6.22450725, 1.06558449, -0.83079853],
[ 1.06558449, 2.82535758, 1.06919888],
[-0.83079853, 1.06919888, 4.34671241]])

다차원 배열의 경우 transpose 메서드는 튜플로 축 번호를 받아서 치환한다. 실제로 계산하려면 머리에 쥐가 날지도 모른다.


In[145]: arr = np.arange(16).reshape((2, 2, 4))
In[146]: arr
Out[143]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],

[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
In[147]: arr.transpose((1,0,2))
Out[144]:
array([[[ 0, 1, 2, 3],
[ 8, 9, 10, 11]],

[[ 4, 5, 6, 7],
[12, 13, 14, 15]]])


.T 속성을 이용하는 간단한 전치는 축을 뒤바꾸는 특별한 경우다. ndarray에는 swapaxes 메서드가 있는데 2개의 축 번호를 받아서 배열을 뒤바꾼다.


In[148]: arr
Out[145]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],

[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
In[149]: arr.swapaxes(1, 2)
Out[146]:
array([[[ 0, 4],
[ 1, 5],
[ 2, 6],
[ 3, 7]],

[[ 8, 12],
[ 9, 13],
[10, 14],
[11, 15]]])

swapaxes도 마찬가지로 데이터를 복사하지 않고 원래 데이터에 대한 뷰를 반환한다.


4.2 유니버설 함수 ufunc라고 불리는 유니버설 함수는 ndarray 안에 있는 데이터 원소별로 연산을 수행하는 함수다. 유니버설 함수는 하나 이상의 스칼라 값을 받아서 하나 이상의 스칼라 결과 값을 반환하는 간단한 함수를 고속으로 수행할 수 있는 벡터화된 래퍼 함수라고 생각하면 된다.


sqrt나 exp같은 간단한 변형을 전체 원소에 적용할 수 있다.


이 경우는 단항 유니버설 함수라 하고,


add나 maximum처럼 2개의 인자를 취해서 단일 배열을 반환하는 함수를 이항 유니버설 함수라고 한다.


In[150]: x = np.random.randn(8)
In[151]: y = np.random.randn(8)
In[152]: x
Out[149]:
array([ 0.57483741, 0.39598833, -0.35815866, -1.73518055, 1.09899127,
0.81740202, 0.82778791, -0.34151437])
In[153]: y
Out[150]:
array([ 0.92638336, 0.70822114, 0.66838903, 0.76546891, 0.22617098,
-1.29688062, 1.9109471 , 2.54965647])
In[154]: np.maximum(x, y)
Out[151]:
array([ 0.92638336, 0.70822114, 0.66838903, 0.76546891, 1.09899127,
0.81740202, 1.9109471 , 2.54965647])


배열 여러개를 반환

'Python for data analysis' 카테고리의 다른 글

Chapter 5 pandas 시작하기  (0) 2016.03.25
Chapter 3. IPython 소개  (0) 2016.03.11
Chapter 2 사례 소개  (0) 2016.03.04
Chaper 1 시작하기 전에  (0) 2016.03.04