Category Archives: Python Basic

Numpy Broadcast 브로드캐스트란?

Numpy 다차원 배열에서 shape이 다른 배열끼리의 연산이 가능하게 하는 기능을 브로드캐스트 Broadcast라고 합니다.

예를 들어 2×2 행렬과 scala 2를 곱할 때 scala 2가 2×2로 확장되면서 두 개의 element wise 곱셈이 가능해지는 것입니다. 여기서 element wise란 각각의 개별 원소 by 원소 라고 보시면 됩니다.

a = np.array([[1,2],[3,4]])  
b = np.array([10, 20])
a * b

위에 예시처럼 element wise 곱을 하면 결과는 아래처럼 b라는 1차원 배열이 2차원으로 확장되어서 곱해집니다. 이렇게 자동으로 곱셈이 가능하도록 확장해주는 것이 브로드캐스트의 기능입니다.

array([[10, 40],
       [30, 80]])

브로드캐스트의 경우 같은 shape의 배열끼리 곱하는 것보다 더 적은 메모리를 차지하기 때문에 좀 더 효율적이라고 볼 수 있습니다.

브로드캐스트를 적용이 가능한 경우와 불가능한 경우를 알아보기 위해 numpy 공식 문서를 번역하여 공유드려보도록 하겠습니다.

출처: https://numpy.org/doc/stable/user/basics.broadcasting.html

두 배열에 대해 처리할 때, numpy는 두 배열의 shape을 element-wise 하게 비교합니다. 이때 차원을 살펴보는데, 두 배열이 호환 가능할 때는 다음과 같습니다.

1. 두 축이 똑같거나 또는
2. 둘 중 하나의 축이 1일때 입니다

이 조건이 충족되지 않으면, ValueError: operands could not be broadcast together라는 에러가 발생하면서 배열이 서로 호환되지 않는 shape임을 나타냅니다.

배열은 반드시 같은 차원의 수를 가질 필요는 없습니다. 예를 들어, RGB 값인 256x256x3 배열의 경우, 이미지에 있는 각 컬러를 다른 값에 의해서 확장하고 싶을 때, 그 이미지를 3개의 값으로 이루어진 1차원 배열과 곱할 수 있습니다. 브로드캐스트 규칙에 따라 이러한 배열의 크기에 대해서 다음과 같이 호환가능함을 알 수 있습니다:

Image (3d array): 256 x 256 x 3
Scale (1d array): 3
Result (3d array): 256 x 256 x 3

둘 중 하나의 차원이 1차원인데, 이 1차원이 확장되어서 다른 배열의 차원과 호환이 가능해집니다.

다음 예시를 보면, A와 B 배열 모두 브로드캐스트를 적용할 때 확장이 가능한 길이인 1을 축으로 가지고 있습니다:

A (4d array): 8 x 1 x 6 x 1
B (3d array): 7 x 1 x 5
Result (4d array): 8 x 7 x 6 x 5

브로드캐스트가 가능한 더 많은 예시는 다음과 같습니다:

A      (2d array):  5 x 4
B      (1d array):      1
Result (2d array):  5 x 4

A      (2d array):  5 x 4
B      (1d array):      4
Result (2d array):  5 x 4

A      (3d array):  15 x 3 x 5
B      (3d array):  15 x 1 x 5
Result (3d array):  15 x 3 x 5

A      (3d array):  15 x 3 x 5
B      (2d array):       3 x 5
Result (3d array):  15 x 3 x 5

A      (3d array):  15 x 3 x 5
B      (2d array):       3 x 1
Result (3d array):  15 x 3 x 5

아래 예시는 브로드캐스트가 적용이 안 되는 경우입니다:

A      (1d array):  3
B      (1d array):  4 # 축의 길이가 다릅니다

A      (2d array):      2 x 1
B      (3d array):  8 x 4 x 3 # A의 축 길이 2와 B의 축 길이 4가 맞지 않습니다

실제 브로드캐스트 예시입니다:

>>> x = np.arange(4)
>>> xx = x.reshape(4,1)
>>> y = np.ones(5)
>>> z = np.ones((3,4))

>>> x.shape
(4,)

>>> y.shape
(5,)

>>> x + y
ValueError: operands could not be broadcast together with shapes (4,) (5,)

>>> xx.shape
(4, 1)

>>> y.shape
(5,)

>>> (xx + y).shape
(4, 5)

>>> xx + y
array([[ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.],
       [ 4.,  4.,  4.,  4.,  4.]])

>>> x.shape
(4,)

>>> z.shape
(3, 4)

>>> (x + z).shape
(3, 4)

>>> x + z
array([[ 1.,  2.,  3.,  4.],
       [ 1.,  2.,  3.,  4.],
       [ 1.,  2.,  3.,  4.]])

브로드캐스트는 두 배열의 간단한 outer product(외적) 또는 outer operation을 제공합니다.
다음의 예시는 두 개의 1차원 배열의 outer addition operation을 보여줍니다.

>>> a = np.array([0.0, 10.0, 20.0, 30.0]) #a.shape = (4,)
>>> b = np.array([1.0, 2.0, 3.0]) #b.shape = (3,)
>>> a[:, np.newaxis] + b #a[:, np.newaxis].shape = (4,1) & b.shape = (3,)
array([[  1.,   2.,   3.],
       [ 11.,  12.,  13.],
       [ 21.,  22.,  23.],
       [ 31.,  32.,  33.]])

여기서 newaxis index operator는 a에 새로운 축을 삽입하여, 2차원의 4×1 배열을 만들어냅니다. (3,) shape을 가진 b와 4×1 배열을 결합하면 4×3 배열이 됩니다.

bincount() 란?

bincount()

non negative integer로 구성된 Numpy array에서 각각의 빈도수를 카운트하는데 사용되는 메소드입니다. 0부터 가장 큰 값까지 각각의 발생 빈도수를 체크합니다.

예를 들어, [3,2,2,6,7,4,8,9,9,9] 라는 array가 있습니다. bincount()를 적용한 값을 출력하면, [0 0 2 1 1 0 1 1 1 3] –> 이렇게 출력되는데요 그 이유는,

import numpy as np

given_array = [3,2,2,6,7,4,8,9,9,9]
answer = np.bincount(given_array)
print(answer)
# the printed result is : [0 0 2 1 1 0 1 1 1 3]

0부터 ~ 9까지 각각의 빈도수를 다 체크한 것이기 때문입니다. 즉, 해당 array에 존재하는 3,2,6,7,4,8,9 총 7가지 숫자의 각각의 발생 빈도수만 기록하는 것이 아니라 0부터 9까지의 빈도수가 다 체크되어 있습니다. 그래서 순서대로 하면 0은 0번, 1은 0번, 2는 2번, 3은 1번, 4는 1번, 5는 0번, 6은 1번, 7은 1번, 8은 1번, 9는 3번 이렇게 해서 [0 0 2 1 1 0 1 1 1 3] 이 리턴이 됩니다.

Pandas Dataframe에서 특정 row, column 출력


# 1. subset by rows
test_df[9:13] # 9 ~ 12 row가 출력됨

# 2. reverse order dataframe
test_df[::-1] # 순서가 거꾸로 출력됨
# 3. get the nth rows only
test_df[::30] # 30번째 행마다 출력됨

# 4. get specific column
test_df['Province_State'] # 특정 칼럼만 출력됨
# 5. get specific columns
test_df[['Date','Country_Region']] # 특정 칼럼들만 출력됨

# 6. get specific row & column
test_df[3:6][['Date','Country_Region']] # 3 ~ 5 행의 Date, Country_Region 칼럼만 출력됨

verbose 란?

verbose 란 사전적 의미로 “말 수가 많다” 인데요 즉, 상세한 로깅 logging 을 출력할지 말지를 조정하는 parameter 라고 보시면 됩니다. 예를 들어, 아래 4번 cell 에서 verbose 값이 2일 때는 “WARNING: ~~ “하고 문구가 있는데요 밑에 5번 cell에서는 verbose 값이 0이기 때문에 코드 실행 시 “WARNING” 이라고 되어있던 부분이 출력되지 않은 것을 확인하실 수가 있습니다.

verbose 값이 0보다 크면 로그를 출력하게되기 때문에 실행 속도에 영향을 줄 수 있습니다. 로그 출력이 속도에 영향을 주는 것에 관해서 개인적인 경험을 공유해드리려고 합니다. 실제로 제가 Speech to Text 음성 변환 API에 대한 Batch Processing 테스트를 진행하였는데요 그 작업 중에 오픈소스로 sox라는 라이브러리를 이용해서 긴 오디오를 묵음 silence 기준으로 상대적으로 작게 쪼개는 과정이 있었는데 print 옵션의 여부가 실행 속도 차이에 미치는 영향이 매우 컸습니다.

참고로 위의 코드 예시는 https://www.tensorflow.org/tutorials/quickstart/beginner?hl=ko 텐서 플로우 튜토리얼입니다.

Python Pandas로 데이터 간단히 살펴보기

Python Pandas로 데이터를 간단히 살펴보도록 하겠습니다 🙂 

먼저 데이터를 읽어옵니다.

# pandas libary 로드하기
import pandas as pd 
# file 읽어오기 pd.read_csv("파일 경로")
test_df = pd.read_csv('./test.csv') 

1. 칼럼 정보 가져오기

test_df.columns

이렇게 하면 칼럼 정보를 가져옵니다. 제가 로드한 데이터는 총 4개의 칼럼으로 구성한 것을 알 수가 있습니다. “id”, “keyword”, “location”, “text” 칼럼이 있습니다.

2. 행 row 가 몇 개인지 체크하기

len(test_df)

3263 개의 행이 있는 데이터입니다.

3. head()를 이용해 앞에 레코드가 어떤 것들이 있는지, tail()을 이용해 뒤에 레코드가 어떤 것들이 있는지 확인합니다.

test_df.head()
test_df.tail()

여기서 만약에 head(5) 또는 tail(10) 처럼 괄호 안에 보고 싶은 레코드 갯수를 적어주면 그 수 만큼의 레코드를 보실 수가 있습니다.

4. 전체 레코드에 대한 요약된 내용 보기

test_df.info()

info()를 이용하면 전체 칼럼이 무엇이 있는지, 전체 행의 수가 몇 개인지, 그리고 전체 레코드 중에서 해당 칼럼에서 missing 된 결측치 (null) 값이 아닌 수가 몇 개 인지를 통해 데이터를 전반적으로 파악할 수가 있습니다. 예를 들어 위의 요약된 내용을 보면 전체 row 행 수는 3263 개이고 id나 text 칼럼은 전체 row 수 만큼 빠짐없이 데이터가 존재하지만 그 외에 keyword는 non-null 값이 3237 개로 3263-3237 = 26개의 데이터가 빠져있다라는 것을 알 수가 있습니다.

나중에 데이터 분석 시에 이렇게 null로 빠져있는 데이터에 대해서 범주형 Categorical 데이터에 대해서는 최빈값(mode)을 채워준다거나 수치형 데이터의 경우 중앙값(median)을 채워준다거나 하는 식의 전처리를 해줄 수가 있습니다.

5. 전체 레코드 중 수치형 데이터에 대한 통계

test_df.describe()

사실 이 데이터의 경우 id만 numeric 수치형 데이터 이기 때문에 통계량을 보기에 적절하지 않을 수가 있는데요 만약에 가격 또는 키와 같은 numeric 데이터에 대해서 위와 같이 평균, 표준오차 등등의 통계량을 얻을 수가 있습니다. 그래서 다른 데이터를 가져와서 describe() 를 실행해보았습니다.

보시면 continuous 한 변수 칼럼들에 대해서 통계량 정보가 나온 것을 확인하실 수가 있습니다.

이상입니다 🙂

Python Pandas로 csv 파일 읽어오기

Python Pandas로 csv를 읽어오는 방법에 대해 알아보도록 하겠습니다 🙂

# pandas 라이브러리 임포트
import pandas as pd
# csv 파일 읽어오기. 괄호 안에는 파일 경로를 적어줍니다.
data = pd.read_csv('./allstate/allstate_test.csv') 
data

그러면 아래처럼 결과가 나옵니다.

추가적으로 만약 파일명을 읽어올 때 현재 주피터 노트북이 실행되었을 때의 경로를 바꾸고 싶으시다면 아래 예시처럼 실행해주면 됩니다.

import os 
print(os.getcwd()) 
os.chdir('<바꾸고 싶은 경로>')