Data Science/Python
[데이터 사이언스] Numpy 속도가 빠른 이유와 속도 비교 예제
FDG
2023. 10. 11. 22:02
Numpy가 빠른 이유¶
for
루프를 사용한 리스트와 NumPy 배열에서 벡터화된 연산 간의 성능 차이는 몇 가지 주요 이유로 인해 발생합니다:¶
1. Overhead: for
루프를 사용하면 반복문을 통해 각 요소에 접근하고 연산을 수행해야 합니다. 이는 반복문의 오버헤드와 Python 인터프리터의 각 반복에서 추가적인 작업이 필요하므로 느린 성능을 초래합니다.¶
2. 메모리 관리: NumPy는 내부적으로 데이터를 연속된 메모리 블록에 저장하므로 데이터에 대한 메모리 액세스가 효율적입니다. 그에 반해 Python 리스트는 객체로 구성되어 있어 각 요소가 독립적인 객체로 메모리에 저장되므로 더 많은 메모리 관리 오버헤드가 발생합니다.¶
3. 컴파일된 코드: NumPy는 C 언어로 작성된 내부 루프와 벡터화된 함수를 사용하여 연산을 수행하므로 Python 반복문보다 훨씬 효율적입니다.¶
4. SIMD 명령어 활용: NumPy는 SIMD(단일 명령, 다중 데이터) 명령어를 활용하여 동시에 여러 요소를 처리할 수 있습니다. 이는 벡터화된 연산을 통해 데이터 처리를 가속화하는 데 도움이 됩니다.¶
5. 최적화: NumPy는 다양한 최적화 기술과 라이브러리를 사용하여 배열 연산을 최적화하고 병렬 처리를 지원합니다.¶
이러한 이유로 인해 NumPy 배열에서 벡터화된 연산은 대량의 데이터를 처리할 때 일반적으로 for
루프를 사용한 리스트보다 빠릅니다.¶
In [52]:
import numpy as np
import time
import pandas as pd
import timeit
Case 1
for문을 이용한 일반 연산¶
In [53]:
# %%timeit -n 5 -r 2
start_time = time.time()
lst = range(1, 1000001)
squared_lst = [x**2 for x in lst]
gap_time_list = time.time() - start_time
squared_lst[-1],gap_time_list
Out[53]:
(1000000000000, 0.49983978271484375)
numpy를 이용한 벡터화 연산¶
In [54]:
# %%timeit -n 5 -r 2
start_time = time.time()
arr = np.arange(1, 1000001,dtype='int64')
squared_arr = arr**2
gap_time_arr = time.time() - start_time
squared_arr[-1],gap_time_arr
Out[54]:
(1000000000000, 0.014994621276855469)
Numpy와 list(for) 속도 비교¶
In [55]:
f'numpy가 list 보다 {gap_time_list/gap_time_arr:0.1f}배 빠름'
Out[55]:
'numpy가 list 보다 33.3배 빠름'
Numpy가 list 보다 33.3배 빠름¶
Case 2
In [56]:
import pandas as pd
import random
genres=['Adventure', 'Animation', 'Children', 'Comedy', 'Fantasy', 'Romance', 'Drama', 'Action', 'Crime', 'Thriller', 'Horror',
'Mystery', 'Sci-Fi', 'War', 'Musical', 'Documentary', 'IMAX', 'Western', 'Film-Noir']
data = []
num_rows = 5000
random.seed(1234)
for _ in range(num_rows):
num_genres = random.randint(1, 6) # 임의의 장르 개수 선택 (1개부터 4개)
random_genres = random.sample(genres, num_genres) # 임의로 선택된 장르 조합
combined_genres = '|'.join(random_genres) # 장르들을 하나의 문자열로 결합
data+=[combined_genres]
df = pd.DataFrame(data, columns=['Combined_Genres']) # 데이터프레임 생성
Pandas¶
In [57]:
# %%time
start_time = time.time()
zero_matrix=np.zeros((len(df),len(genres)))
dummies=pd.DataFrame(zero_matrix,columns=genres)
for i, gen in enumerate(df.Combined_Genres):
indices=dummies.columns.get_indexer(gen.split('|'))
dummies.iloc[i,indices]=1
movies_windic=df.join(dummies.add_prefix('genre_'))
# movies_windic
gap_time_df = time.time() - start_time
gap_time_df
Out[57]:
1.6244792938232422
Numpy¶
In [58]:
# %%time
start_time = time.time()
gen=df.iloc[:,0].str.split('|')
matrix = np.zeros((len(df), len(genres)), dtype=int)
genre_to_idx = {genre: idx for idx, genre in enumerate(genres)}
for i in range(len(df)):
for item in gen[i]:
idx = genre_to_idx.get(item)
matrix[i, idx] = 1
prefix_genres=['genre_'+x for x in genres]
matrix_df=pd.DataFrame(matrix,columns=prefix_genres)
new_movies=df.join(matrix_df)
# new_movies.iloc[:,3:-1]=matrix_df
gap_time_np = time.time() - start_time
gap_time_np
Out[58]:
0.05196881294250488
Numpy와 Pands 속도 비교¶
In [59]:
print(f'결과값 비교: {"같음" if (movies_windic==new_movies).all().all() else "다름"}')
print(f'numpy가 pandas 보다 {gap_time_df/gap_time_np:0.1f}배 빠름')
결과값 비교: 같음
numpy가 pandas 보다 31.3배 빠름