728x90
반응형

 지금까지 scalar, list Type에 대해서 알아보았다. 이제 남은 대표적인 Type은 array, tensor, dictionary, DataFrame이 있는데, 이들은 앞서 다뤘던 두 Type에 비해 훨씬 심도 깊은 학습이 필요하므로, 특징만 간략히 설명하고 넘어가겠다.

 

 

array

Numpy의 array 

  • Python의 단점을 이야기할 때, 흔히들 느린 속도를 꼽는데, 이 이미지를 한 번에 종식시킬 수 있는 것이 바로 Numpy라는 모듈이며, 그 Numpy 모듈의 기본 Type이 바로 array다.
  • Numpy는 C언어, Fortran을 기반으로 만들어졌기 때문에 연산 속도가 매우 빠르며, 쉬운 것이 장점인 파이썬으로 구현되어 있기에 C언어를 공부하지 않고도 복잡한 수학 연산 문제를 아주 쉽고 빠르게 접근할 수 있다.
  • 특히 데이터 분석가의 친구인 판다스의 단점인 느린 속도를 해결할 수 있기 때문에 데이터 분석을 하고자 한다면, Numpy를 아주 잘 다룰 수 있어야 한다.
  • Numpy의 array는 Scipy, tensorflow, sklearn, Matplotlib, Pandas 등 빅데이터 분석에서 필수로 사용되는 모듈을 활용하는 데에 있어 기초가 되어준다.
  • 보다 상세한 내용은 추후 Numpy에 대해 자세히 학습할 때 이야기해보도록 하자.
# array를 사용하기 위해선 numpy 모듈을 가지고 와야한다.
>>> import numpy as np

# array는 list를 만들고 np.array() 함수에 넣어서 생성하기도 한다.
>>> a = [1,2,3,4,5]
>>> a_array = np.array(a)
>>> a_array
array([1, 2, 3, 4, 5])
  • numpy 모듈을 불러오는 import numpy as np는 "numpy를 수입(import) 해오겠다 np 처럼(as)" 이라고 곧이곧대로 해석해도 된다. 
    1. 이게 파이썬의 대표적인 장점 중 하나로 꼽히는 파이썬 코드가 우리가 실제로 사용하는 언어와 굉장히 가깝다는 것을 보여주는 대표적인 예시중 하나이다.
  • np는 numpy의 줄임말이며, Python은 "."을 기준으로 하여, 모듈로부터 하위 함수로 한 단계 한단계 내려가는 형태를 가지고 있다.
    • 즉, np.array()는 numpy 모듈의 array() 함수라는 뜻이다.
# array에 담겨있는 data type(dtype)은 상당히 중요하다.
>>> a_array.dtype
dtype('int32')


# 소수(float)로 구성된 array를 만들어보자
>>> b = np.arange(1, 10, 2, dtype = "float")
>>> b
array([1., 3., 5., 7., 9.])

>>> b.dtype
dtype('float64')
  • array.dtype은 array의 data type을 알 수 있다.
  • np.arange()은 앞서 list의 range와 비슷한 기능을 하며, array를 바로 생성한다.
  • array 내 data를 정수로 입력했다 할지라도 dtype을 "float"으로 지정하면 소수로 생성된다.

 

 

array 연산과 Broadcast

# Numpy의 array 연산은 R의 Vector 연산과 굉장히 유사하다.
>>> c = np.array([1, 3, 5, 7, 9])
>>> d = np.array([2, 4, 6, 8, 10])
>>> e = np.array([10, 20])


# 자리수가 동일한 array(vector)끼리는 동일한 위치의 원소끼리 연산이 이루어진다.
>>> print(c+d) 
[ 3  7 11 15 19]
>>> print(c*d)
[ 2 12 30 56 90]
>>> print(c/d)
[0.5        0.75       0.83333333 0.875      0.9]


# 자리수가 동일하지 않은 array(vector)간의 연산은 이루어지지 않는다.
>>> print(c + e)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-12-3b9c09c64b20> in <module>
----> 1 print(c + e)

ValueError: operands could not be broadcast together with shapes (5,) (2,) 
  • Numpy의 array 연산을 보면 R의 Vector 연산과 꽤 유사한 것을 알 수 있다.
  • Numpy의 array는 Tensorflow의 기본 Type인 Tensor와 거의 동일한 개념으로 봐도 문제없다.
    1. Tensor란 다차원 배열(array)을 아우르는 말이며, 이 안에는 1차원 배열인 Vector와 2차원 배열인 Metrics를 포함하는 것이다.
    2. 선형 대수학의 관점에서 접근할 때, array에 보다 익숙해지기 위해 1차원 array는 단순하게 vector로 부르도록 하겠다.
  • shape(모양)이 동일하지 않은 array끼리 연산 시, ValueError가 발생한다.
# BroadCast
>>> c
array([1, 3, 5, 7, 9])

>>> c * 2
array([ 2,  6, 10, 14, 18])

>>> c + 2
array([ 3,  5,  7,  9, 11])

>>> c / 2
array([0.5, 1.5, 2.5, 3.5, 4.5])
  • 1차원 array에 scalar 값을 연산 시, array의 모든 원소에 scalar값이 연산된다.
# array를 여러개 쌓으면 행렬이 된다.
>>> f = np.array([[1, 3, 5, 7],[2, 4, 6, 8]])
>>> f * 2
array([[ 2,  6, 10, 14],
       [ 4,  8, 12, 16]])
 
 
# 행렬에 대한 Broadcast
>>> f = np.array([[1, 3, 5, 7],[2, 4, 6, 8]])
>>> f * np.array([10, 20, 30, 40])
array([[ 10,  60, 150, 280],
       [ 20,  80, 180, 320]])
  • m*n행렬에 대해 스칼라 값을 연산하거나  n*1 벡터를 연산하면 array의 Broadcast와 같은 방식으로 연산이 이루어진다.
#  행렬 곱
>>> f = np.array([[1, 3, 5, 7],[2, 4, 6, 8]])
>>> f * np.array([[10, 10, 10, 10], [20, 20, 20, 20]])
array([[ 10,  30,  50,  70],
       [ 40,  80, 120, 160]])
       

>>> g = np.array([[10,10],[20,20],[30,30],[40,40]])
>>> np.dot(f,g)
array([[500, 500],
       [600, 600]])
  • 형태(shape)가 동일한 행렬끼리 *+-/와 같은 연산 수행 시, 동일한 위치에 있는 원소끼리 곱해진다(이는 우리가 일반적으로 아는 행렬곱이 아니다.).
  • m*n행렬, n*o행렬과 같이 행렬곱이 가능한 대상을 np.dot(mat1, mat2)을 하는 경우 우리가 아는 행렬곱이 연산된다.

 

 

배열의 형태

# array의 shape
>>> vt1 = np.array([1,2,3,4])
>>> vt2 = np.arange(1, 5, 0.1)
>>> mat1 = np.array([[1,3,5,7],[2,4,6,8]])
>>> mat2 = np.array([[1,10],[2,20],[3,30],[4,40]])

>>> print(vt1.shape)
(4,)
>>> print(vt2.shape)
(40,)
>>> print(mat1.shape)
(2, 4)
>>> print(mat2.shape)
(4, 2)


# 형태 변환(reshape)
>>> vt1.reshape((4,1))
array([[1],
       [2],
       [3],
       [4]])
>>> mat2.reshape((2,4))
array([[ 1, 10,  2, 20],
       [ 3, 30,  4, 40]])
       
       
# 평활(Flatten)
>>> mat1
array([[1, 3, 5, 7],
       [2, 4, 6, 8]])
>>> mat1.flatten()
array([1, 3, 5, 7, 2, 4, 6, 8])

>>> mat2
array([[ 1, 10],
       [ 2, 20],
       [ 3, 30],
       [ 4, 40]])
>>> mat2.flatten()
array([ 1, 10,  2, 20,  3, 30,  4, 40])
  • 앞서 이야기했던 array의 shape은 Numpy를 다루는 데 있어 필수 사항이며, 나아가 딥러닝을 다루는데 주로 사용되는 모듈인 tensorflow를 사용할 때도 매우 중요하다.
  • 상당수의 연산 오류는 shape이나 dtype이 일치하지 않아 발생한다.
  • reshape() 함수를 이용해서 array의 형태를 바꿀 수 있다.
  • 우리가 일반적으로 2차원으로 아는 행렬은 flatten() 함수를 통해 수월하게 벡터화되며, 이는 텐서플로우 학습 시, 상당히 중요한 내용이다.
    1. 행렬이 1차원 배열로 변환 가능하다는 것은 우리가 아는 2차원으로 알고 있는 행렬은 사실 상 1차원임을 의미한다. 이에 대해선 추후 Numpy를 설명하면서 보다 자세히 짚고 넘어가도록 하겠다.

 

 

 Numpy의 핵심 Type인 array는 이를 공부하는 데만 해도 상당한 시간을 투자해야 하기 때문에 이번 포스트에서는 가장 기본적인 array의 성격들만 알아보았다.

 앞서 list를 학습할 때, 발생했던 상당 부분의 list의 한계점으로 여겨졌던 부분들은 array를 통해 대부분 해결 가능하다.

 또 다른 Type인 tensorflow의 tensor는 numpy의 array와 굉장히 유사하며, 함수에 약간의 차이가 있긴하나 거의 동일한 기능들이 존재한다. 또한, tensorflow 학습 시, keras를 위주로 쓰게 되며, 굳이 tensor로 연산하기보다 numpy의 array로 연산하는 것을 추천한다. 

  1. tensorflow에서 tensor를 다루는 함수는 numpy의 array를 다루는 함수와 기능이 거의 일치한다. 굳이 tensor를 다루는 함수를 공부하지 않아도 tensorflow를 다루는데 큰 지장이 없다.
  2. tensorflow에 대해 추후 학습하겠으나, tensorflow 2.0 version에 들어오며, keras를 주로 활용하여 딥러닝을 실시하게 될 것이다.
  3. 상세한 내용은 python-Numpy 카테고리에서 포스팅하도록 하겠다.
728x90
반응형
728x90
반응형

 이전 포스트에선 빅데이터 분석을 하며 눈에 익었던 Python Type이 무엇이 있는지와 그중 하나인 Scalar에 대해 간략하게 알아보았다. 이번 포스트에서는 list에 대해 이야기해보자.

 

 

list

# list는 Data를 담는 가장 기본적인 상자라고 생각하자
# list는 다음과 같은 방법으로 만들 수 있다.
# 1. [] 사용하기
>>> list1 = [1, 2, 3, 4, 5, 6, 7]
>>> print(list1)
[1, 2, 3, 4, 5, 6, 7]

# 2. list() 함수 사용하기
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • list는 파이썬에서 가장 일반적인 데이터를 담는 type이다.
  • scalar(int, float, None, Boolean)뿐만 아니라 문자형(str)도 담을 수 있다.
  • 연산에 특화된 Type이 아닌 데이터를 담는 기본 그릇이라고 생각하는 것이 이해하기 쉽다.
  • list는 list() 함수, [] 안에 넣고자하는 Data를 넣어서 만들 수 있다.

 

Range

# range는 무엇인가?
>>> print(range(10))
range(0, 10)

# for문을 사용하여 range의 인자들을 출력해보자
>>> for i in range(10):
>>>    print(i)
0
1
2
3
4
5
6
7
8
9

# range()함수는 range(시작, 끝, 간격)으로 값을 내가 원하는 때에 호출할 수 있는 함수다.
>>> [i for i in range(4, 20, 2)]
[4, 6, 8, 10, 12, 14, 16, 18]
  • range() 함수는 꽤 자주 쓰이는 함수로, list와 꽤 친하니 이참에 설명하고 가겠다.
  • range(시작 값, 끝 값, 간격)을 입력하여 내가 원하는 패턴을 갖는 임의의 데이터를 생성할 수 있다.
  • 중요사항!) list에 data를 담는 경우, 그 data를 전부 생성해버리기 때문에 list를 생성하는 순간부터 memory를 쭉 잡아먹지만, range() 함수는 내가 원할 때, 그 값을 가지고 오기 때문에 memory 낭비가 발생하지 않는다.
    1. range() 함수를 출력하는 경우 range()라는 객체가 반환된다.
      • 객체란? 아주 단순하게 설명해보자면, X라는 함수를 통해 무언가를 만들어냈으나, 그 결과물이 가지고 있는 정보가 매우 다양해서 한 번에 다 보는 것이 어려운 상태이다.
      • 이 결과물에 특정 스위치를 붙이고 스위치를 켜주면, 그에 해당하는 정보를 볼 수 있다.
      • 즉, "내가 보여줄 수 있는 것은 아주 많은데, 뭘 원해?"라고 물어보고 있는 상태라고 생각하면 쉽다.
    2.  range() 함수로 생성된 range 객체는 당장 값이 나오지 않으며, for문과 같은 기능을 실행해주어야, 값을 반환한다.
      • 내가 원할 때, 값을 가지고 온다(for문을 실행)는 것은 제네레이터(Generator)와 유사해보이지만, range()는 엄밀히 따지면 Genarator가 아니다.
      • next() 함수를 이용해서 값을 꺼내면 오류가 발생한다.
      • 이는 추후 제네레이터를 설명할 때 다시 언급하도록 하겠다.

 

 

 

list의 연산

>>> a = [1,2,3,4]
>>> b = [5,6,7]
>>> print(a + b)
[1, 2, 3, 4, 5, 6, 7]


>>> print(a * b)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-f9c8a447c001> in <module>
----> 1 print(a*b)

TypeError: cant multiply sequence by non-int of type "list"


>>> print(a * 3)
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
  • Data가 담기는 그릇인 list는 연산보다는 Data를 담는 것이 주목적이다 보니 우리가 생각하는 연산과 다른 결과가 출력된다.
  • list와 list를 더 하면 두 list의 값이 합쳐지는(sum)것이 아닌 한 그릇으로 합쳐진다(extend).
  • list와 list의 곱은 데이터가 담긴 두 그릇을 곱하는 것이니 연산되지 않는다.
  • list에 int를 곱하면 데이터가 담긴 그릇을 그 int만큼 더 추가한다.
  • 이는 list가 연산이 아닌 데이터를 담는 그릇의 성질이 더 강하다는 것을 뜻한다. 때문에 list에는 숫자형과 문자형을 함께 담을 수도 있으며, 함께 담는다고 해서 데이터의 타입이 자동으로 바뀌지 않는다.

 

 

 

list의 인덱싱(슬라이싱)

# 내가 원하는 위치에 있는 값을 가지고 올 수 있다.
>>> c = list(range(20))
>>> print(c[3])
3
>>> print(c[5:12])
[5, 6, 7, 8, 9, 10, 11]

# list의 안에는 다른 list를 담을수도 있다!
>>> d = [3,5,7,['a', 'p', 'p', 'l', 'e']]
>>> print(d[3])
['a', 'p', 'p', 'l', 'e']
>>> print(d[3][3:])
['l', 'e']
  • Data를 담는 그릇인 list는 아주 쉽게 내가 원하는 위치에 있는 데이터를 가지고 올 수 있다.
  • list[위치]를 이용하면 그 위치에 있는 값을 가지고 온다.
  • Data를 담는 그릇인 list는 list 안에 list를 담을 수 있는데, 안에 있는 list를 가지고 오고 그 list 안에서 원하는 위치를 지정하면 똑같이 가져올 수 있다.
  • :는 가져오려는 index의 구간을 정해줄 수 있다.
# list에서 특정 조건에 맞는 값을 가지고 오기.
>>> print(c)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]


# 1. list c에서 10 이상인 값들을 가지고 와보자.
>>> c >= 10
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-36-0a29ddccf71f> in <module>
----> 1 c >= 10

TypeError: '>=' not supported between instances of 'list' and 'int'


# 2. for문과 if문을 혼용하여 가져와보자
>>> result = []
>>> for i in c:
>>>     if i >= 10:
>>>         result.append(i)
>>>     
>>> print(result)
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

# 3. 위 코드보다 단순하게 적어보자
>>> [i for i in c if i >= 10]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
  • R이나 파이썬 numpy의 array는 c[c>=10] 처럼 단순한 코드로 조건에 해당하는 값을 가지고 올 수 있으나, list는 이 방법으로 data를 가지고 올 수 없다.
  • if문을 사용하여야하며, 아직 익숙하지 않은 사람이라면 "# 2. 주석"대로 코드를 짜는 것이 쉬우며, 어느 정도 코드에 익숙해진 후에는 "# 3. 주석"의 코드 같이 짧은 코드로 짜도록 하자.

 

 

 

기타 리스트에서 자주 쓰이는 함수

# 1. append: list에 값 추가
>>> e = []
>>> for i in range(2, 10, 2):
>>>     e.append(i)
>>>     
>>> print(e)
[2, 4, 6, 8]

>>> e0 = [1,3,5]
>>> e0.append([2,4,5])
>>> print(e0)
[1, 3, 5, [2, 4, 5]]




# 2. extend: list에 다른 list의 원소들을 추가하기
>>> e.extend([1,3,5,7])
>>> print(e)
[2, 4, 6, 8, 1, 3, 5, 7]




# 3. sort: 정렬
>>> e.sort(reverse = True)
>>> print(e)
[8, 7, 6, 5, 4, 3, 2, 1]

>>> e.sort()
>>> print(e)
[1, 2, 3, 4, 5, 6, 7, 8]




# 4. insert: 삽입
>>> e.insert(2, 20)
>>> print(e)
[1, 2, 20, 3, 4, 5, 6, 7, 8]




# 5. pop: 뽑아내기
>>> e1 = e.pop(2)
>>> print(e1)
20
>>> print(e)
[1, 2, 3, 4, 5, 6, 7, 8]




# 6. remove: 가장 왼쪽에 있는 해당하는 값 제거
>>> f = [4,5,6] * 3
>>> f.remove(5)
>>> print(f)
[4, 6, 4, 5, 6, 4, 5, 6]




# 7. len: list 내 원소의 갯수 세기
>>> print(len(f))
8




# 8. count: list 내 특정 원소의 갯수 세기
>>> print(f.count(5))
2
  • list.append(value)는 list에 다른 값(list도 하나의 원소로써 추가 가능!)을 추가하는 함수다.
  • list.extend(list)는 list + list랑 같다
  • list.sort(reverse = False)는 자주 쓰이는 함수인데, reverse는 역순으로 정렬하느냐를 의미한다. 기본적으로 reverse = False로 지정돼있으며, 이는 오름차순(작은 순)이다.
  • 그냥 list의 순서를 거꾸로 하고 싶다면 list.reverse()를 쓰자
  • list.insert(위치, 값) 함수를 이용하면 내가 원하는 위치에 원하는 값을 넣을 수 있다.
  • list.pop(위치) 함수는 해당 위치에 있는 value를 아예 뽑아버린다!
  • list.remove(값) 함수는 가장 왼쪽에 있는 해당 값만 제거하므로 모두 제거하고 싶다면 for문이나 while문으로 반복실행하자
  • len(list) 함수는 list 내 원소의 수를 가지고 온다.
  • len(문자) 함수는 문자의 길이를 반환한다.
  • list.count(value)는 list 내 value의 개수를 가져오는 유용한 함수다.

 

 지금까지 list type에 대해 빠르게 훑어봤는데, 파이썬의 가장 기초가 되는 Data를 담는 type이다 보니 꽤 자주 쓰이는 type이다.

 위의 list 관련 함수들을 보다 보면, 분석가가 바라는 기능에 비해 조금씩 나사가 빠져있어, 원하는 결과를 내기 위해선 for문이나 while문을 섞어줘야 하는 경우가 꽤 있다.

 이 것이 본문에서 "list는 Data를 담는 그릇이지, 연산을 위한 Type은 아니다"라 한 이유이기도 한데, 이후에 학습할 array, DataFrame 등은 연산이나 데이터 정리에 훨씬 특화돼있기 때문에 연산이나 Indexing 등에서 훨씬 쉽고 우월한 성능을 낸다.

 이후 포스트에서 다룰 array와 DataFrame 등은 그 기능이 굉장히 다양하고, 이를 설명하려면 어지간한 전공서적만큼 설명이 필요하므로, 나머지 Type 포스팅에선 살짝 찍어만 먹어보고, 다른 카테고리인 Python-Pandas와 Python-Numpy에서 자세히 다뤄보자.

728x90
반응형

+ Recent posts