import numpy as npimport pandas as pd# pandas optionspd.set_option('mode.copy_on_write', True) # pandas 2.0pd.options.display.float_format ='{:.2f}'.format# pd.reset_option('display.float_format')pd.options.display.max_rows =7# max number of rows to display# NumPy optionsnp.set_printoptions(precision =2, suppress=True) # suppress scientific notation
Numpy & pandas
Python 언어는 수치 계산을 위해 디자인되지 않았기 때문에, 데이터 분석에 대한 효율적이고 빠른 계산이 요구되면서 C/C++이라는 언어로 구현된 NumPy (Numerical Python)가 탄생하였고, Python 생태계 안에 통합되었음. 기본적으로 Python 언어 안에 새로운 언어라고 볼 수 있음. 데이터 사이언스에서의 대부분의 계산은 NumPy의 ndarray (n-dimensioal array)와 수학적 operator들을 통해 계산됨.
데이터 사이언스가 발전함에 따라 단일한 floating-point number들을 성분으로하는 array들의 계산에서 벗어나 칼럼별로 다른 데이터 타입(string, integer, object..)을 포함하는 tabular 형태의 데이터를 효율적으로 처리해야 할 필요성이 나타났고, 이를 다룰 수 있는 새로운 언어를 NumPy 위에 개발한 것이 pandas임. 이는 기본적으로 Wes Mckinney에 의해 독자적으로 개발이 시작되었으며, 디자인적으로 불만족스러운 점이 지적되고는 있으나 데이터 사이언스의 기본적인 언어가 되었음.
Python의 기본 리스트를 사용할 때는 각 요소에 대해 반복문(for loop)이나 list comprehension을 사용해야 하지만, NumPy의 vectorized operation은
속도: 수십 배에서 수백 배까지 빠름
코드 간결성: 반복문 없이 수학적 표현 그대로 작성 가능
메모리 효율성: 내부적으로 최적화된 메모리 접근
vectorized operation이 빠른 이유
Pre-compiled C code: NumPy의 내부 연산은 C/C++로 작성되어 컴파일된 코드로 실행됨
Contiguous memory: 데이터가 메모리에 연속적으로 배치되어 있어 캐시 효율성이 높음
SIMD (Single Instruction, Multiple Data): 현대 CPU의 벡터 연산 명령어를 활용하여 한 번의 명령으로 여러 데이터를 동시에 처리
No Python overhead: Python interpreter의 오버헤드(타입 체크, 함수 호출 등)를 반복하지 않음
반면, Python의 list comprehension은: - 각 요소마다 Python interpreter를 거쳐야 함 - 타입 체크와 메모리 할당이 반복적으로 발생 - 최적화되지 않은 메모리 접근 패턴
제곱 계산 비교
# 큰 데이터 생성n =1_000_000python_list =list(range(n))numpy_array = np.arange(n)print(f"python_list: {python_list[:10]}")print(f"numpy_array: {numpy_array[:10]}")
2.66 ms ± 29.6 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeitimport math# list comprehension + math modulem =sum(python_list) /len(python_list)s = math.sqrt(sum((xi - m)**2for xi in python_list) /len(python_list))z = [(xi - m) / s for xi in python_list]
121 ms ± 4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
왜 이런 차이가 발생하는가?
# math 모듈 + list comprehension[math.sqrt(x) for x in data]
→ Python interpreter → 값 꺼내기 → Python → C 호출 → C 실행 → Python 반환 → 리스트 추가 → 반복 100,000번
# NumPy vectorizednp.sqrt(data)
→ Python → C 호출 단 1번 → C 코드가 전체 배열을 메모리에서 연속적으로 처리 (SIMD 활용) → Python 반환 단 1번