기존 열에서 새 열을 생성하기 위해 적용되는 판다 성능 대 np.vectorize
저는 Pandas 데이터 프레임을 사용하고 있으며 기존 열의 함수로 새 열을 만들고 싶습니다.사이의 속도 차이에 대한 좋은 논의를 보지 못했습니다.df.apply()그리고.np.vectorize()그래서 저는 여기에 물어보려고 했습니다.
apply()기능이 느립니다.제가 측정한 바에 따르면(아래 일부 실험에서 보여짐),np.vectorize() 함수 DataFrame을 빠름apply()적어도 2016년 맥북 프로에서는.이것은 예상된 결과이며, 그 이유는 무엇입니까?
를 들어, 다음과 같은 에 예를들어, 다과같프레있가다정니가 있다고 .N복사:
N = 10
A_list = np.random.randint(1, 100, N)
B_list = np.random.randint(1, 100, N)
df = pd.DataFrame({'A': A_list, 'B': B_list})
df.head()
# A B
# 0 78 50
# 1 23 91
# 2 55 62
# 3 82 64
# 4 99 80
두의 열의 가정해 .A그리고.B아래예서는간기사능용니다합을단한제에▁a다▁▁in▁use▁▁illdivide()그 기능을 적용하기 위해, 나는 다음 중 하나를 사용할 수 있습니다.df.apply()또는np.vectorize():
def divide(a, b):
if b == 0:
return 0.0
return float(a)/b
df['result'] = df.apply(lambda row: divide(row['A'], row['B']), axis=1)
df['result2'] = np.vectorize(divide)(df['A'], df['B'])
df.head()
# A B result result2
# 0 78 50 1.560000 1.560000
# 1 23 91 0.252747 0.252747
# 2 55 62 0.887097 0.887097
# 3 82 64 1.281250 1.281250
# 4 99 80 1.237500 1.237500
가 내가늘을 ,N 개로, 는 100만 개 이상의 크기로 합니다.np.vectorize() 빠릅니다.df.apply().
다음은 완전한 벤치마킹 코드입니다.
import pandas as pd
import numpy as np
import time
def divide(a, b):
if b == 0:
return 0.0
return float(a)/b
for N in [1000, 10000, 100000, 1000000, 10000000]:
print ''
A_list = np.random.randint(1, 100, N)
B_list = np.random.randint(1, 100, N)
df = pd.DataFrame({'A': A_list, 'B': B_list})
start_epoch_sec = int(time.time())
df['result'] = df.apply(lambda row: divide(row['A'], row['B']), axis=1)
end_epoch_sec = int(time.time())
result_apply = end_epoch_sec - start_epoch_sec
start_epoch_sec = int(time.time())
df['result2'] = np.vectorize(divide)(df['A'], df['B'])
end_epoch_sec = int(time.time())
result_vectorize = end_epoch_sec - start_epoch_sec
print 'N=%d, df.apply: %d sec, np.vectorize: %d sec' % \
(N, result_apply, result_vectorize)
# Make sure results from df.apply and np.vectorize match.
assert(df['result'].equals(df['result2']))
결과는 다음과 같습니다.
N=1000, df.apply: 0 sec, np.vectorize: 0 sec
N=10000, df.apply: 1 sec, np.vectorize: 0 sec
N=100000, df.apply: 2 sec, np.vectorize: 0 sec
N=1000000, df.apply: 24 sec, np.vectorize: 1 sec
N=10000000, df.apply: 262 sec, np.vectorize: 4 sec
한다면np.vectorize()일반적으로 항상 보다 빠릅니다.df.apply()그렇다면 왜일까요?np.vectorize()더 이상 언급되지 않았습니까?는 "StackOverflow"와 .df.apply()를 들어 다음과 같습니다.
판다를 여러 열에 '적용' 기능을 사용하려면 어떻게 해야 합니까?
Pandas 데이터 프레임의 두 열에 함수를 적용하는 방법
먼저 Pandas와 NumPy 어레이의 성능은 수치 1어레이의 고성능 벡터화 계산에서 도출됩니다.벡터화된 계산의 전체 포인트는 계산을 고도로 최적화된 C 코드로 이동하고 연속 2메모리 블록을 사용하여 파이썬 수준의 루프를 피하는 것입니다.
파이썬 수준 루프
이제 우리는 몇 가지 타이밍을 볼 수 있습니다.다음은 다음 중 하나를 생성하는 모든 파이썬 레벨 루프입니다.pd.Series,np.ndarray또는list동일한 값을 포함하는 개체입니다.데이터 프레임 내의 영상 시리즈에 할당하기 위해 결과는 비교 가능합니다.
# Python 3.6.5, NumPy 1.14.3, Pandas 0.23.0
np.random.seed(0)
N = 10**5
%timeit list(map(divide, df['A'], df['B'])) # 43.9 ms
%timeit np.vectorize(divide)(df['A'], df['B']) # 48.1 ms
%timeit [divide(a, b) for a, b in zip(df['A'], df['B'])] # 49.4 ms
%timeit [divide(a, b) for a, b in df[['A', 'B']].itertuples(index=False)] # 112 ms
%timeit df.apply(lambda row: divide(*row), axis=1, raw=True) # 760 ms
%timeit df.apply(lambda row: divide(row['A'], row['B']), axis=1) # 4.83 s
%timeit [divide(row['A'], row['B']) for _, row in df[['A', 'B']].iterrows()] # 11.6 s
몇 가지 장점:
- 그
tuple기반 방법(처음 4)은 다음보다 효율적인 요소입니다.pd.Series기반 메서드(마지막 3개). np.vectorize이해력 + 이해력 +zip그리고.map방법, 즉 상위 3개는 모두 거의 동일한 성능을 가지고 있습니다.이 그이사때문다니입기하용을 사용하기 입니다.tuple그리고 머리 위에 있는 판다들을 우회합니다.pd.DataFrame.itertuples.- 를 사용하면 속도가 크게 향상됩니다.
raw=True와 함께pd.DataFrame.apply없는 것과 없는 것.을 사용자 합니다. NumPy 배열은 사용자 정의 함수가 .pd.Series물건들.
pd.DataFrame.apply 또 다른 ㅠㅠㅠㅠㅠ
Pandas가 전달하는 물체를 정확하게 보려면 기능을 조금씩 수정할 수 있습니다.
def foo(row):
print(type(row))
assert False # because you only need to see this once
df.apply(lambda row: foo(row), axis=1)
출력:<class 'pandas.core.series.Series'>Pandas 시리즈 개체를 만들고 전달하고 쿼리하면 NumPy 배열에 비해 상당한 오버헤드가 발생합니다.이것은 놀라운 일이 아닙니다: 판다 시리즈에는 지수, 가치, 속성 등을 담을 수 있는 상당한 양의 발판이 포함되어 있습니다.
해보세요.raw=True그리고 당신은 보게 될 것입니다.<class 'numpy.ndarray'>이 모든 것이 문서에 설명되어 있지만, 보는 것이 더 설득력이 있습니다.
np.vectorize 벡터화
의 문서에는 다음과 같은 참고 사항이 있습니다.
는 벡화된함다평가니다합을음을 평가합니다.
pyfuncnumpy의 브로드캐스트 규칙을 사용하는 것을 제외하고 파이썬 맵 함수와 같은 입력 배열의 연속적인 튜플 위에.
입력 배열의 차원이 동일하기 때문에 "방송 규칙"은 여기서 중요하지 않습니다. 것사것과 한 것map교육적입니다, 왜냐하면.map위 버전의 성능은 거의 동일합니다.소스 코드는 다음과 같은 상황을 보여줍니다.np.vectorize를 통해 입력 함수를 범용 함수("ufunc")로 변환합니다.캐싱과 같은 몇 가지 최적화 기능이 있어 성능이 다소 향상될 수 있습니다.
대컨요,np.vectorize파이썬 레벨 루프가 수행해야 하는 작업을 수행하지만,pd.DataFrame.apply오버헤드가 많이 발생합니다.JIT 컴파일은 없습니다(아래 참조).그냥 편의상입니다.
실제 벡터화: 사용해야 할 항목
위의 차이점은 왜 어디에도 언급되어 있지 않습니까?실제로 벡터화된 계산의 성능은 관련이 없기 때문입니다.
%timeit np.where(df['B'] == 0, 0, df['A'] / df['B']) # 1.17 ms
%timeit (df['A'] / df['B']).replace([np.inf, -np.inf], 0) # 1.96 ms
예, 이는 위의 루프 솔루션 중 가장 빠른 솔루션보다 최대 40배 더 빠른 속도입니다.다음 중 어느 것이든 허용됩니다.제 생각에, 첫 번째는 간결하고, 읽기 쉽고, 효율적입니다. 다른방예법예)만 보십시오.numba성능이 중요하고 병목 현상의 일부일 경우 아래와 같이 처리할 수 있습니다.
numba.njit 향상
루프가 실행 가능한 것으로 간주되는 경우 일반적으로 다음을 통해 최적화됩니다.numbaC로 최대한 많이 이동할 수 있도록 기본 NumPy 배열을 사용합니다.
실로제로▁indeed실.numba성능을 마이크로초로 향상시킵니다.일부 번거로운 작업 없이는 이보다 훨씬 더 효율적으로 작업하기가 어려울 것입니다.
from numba import njit
@njit
def divide(a, b):
res = np.empty(a.shape)
for i in range(len(a)):
if b[i] != 0:
res[i] = a[i] / b[i]
else:
res[i] = 0
return res
%timeit divide(df['A'].values, df['B'].values) # 717 µs
용사를 합니다.@njit(parallel=True)더 큰 어레이에 추가적인 성능 향상을 제공할 수 있습니다.
1 숫자 유형은 다음과 같습니다.int,float,datetime,bool,category제외합니다. objectdtype 및 연속 메모리 블록에 보관할 수 있습니다.
2 Python에 비해 NumPy 작업이 효율적인 이유는 최소 2가지입니다.
- 파이썬의 모든 것은 객체입니다.여기에는 C와 달리 숫자가 포함됩니다.따라서 파이썬 유형에는 네이티브 C 유형에는 존재하지 않는 오버헤드가 있습니다.
- NumPy 방법은 일반적으로 C 기반입니다.또한 가능한 경우 최적화된 알고리즘이 사용됩니다.
((감, 소이))가 .numpy자체 내부로 이동할 수 있음), 성능이 크게 다르지 않음을 알게 될 것입니다.예:
name_series = pd.Series(np.random.choice(['adam', 'chang', 'eliza', 'odom'], replace=True, size=100000))
def parse_name(name):
if name.lower().startswith('a'):
return 'A'
elif name.lower().startswith('e'):
return 'E'
elif name.lower().startswith('i'):
return 'I'
elif name.lower().startswith('o'):
return 'O'
elif name.lower().startswith('u'):
return 'U'
return name
parse_name_vec = np.vectorize(parse_name)
몇 가지 시간을 지정하는 중:
적용 사용
%timeit name_series.apply(parse_name)
결과:
76.2 ms ± 626 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
용사를 합니다.np.vectorize
%timeit parse_name_vec(name_series)
결과:
77.3 ms ± 216 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Numpy로 .ufunc호출할 때 객체np.vectorize어떻게 이런 일을 하는지, 저는 잘 모르겠습니다. 제가 기꺼이 ATM을 이용하는 것보다 더 많은 numpy의 내부를 파헤쳐야 할 것입니다.그렇긴 하지만, 여기서는 문자열 기반의 함수보다 단순한 수치 함수에서 더 잘 하는 것 같습니다.
크기를 최대 1,000,000까지 크랭킹:
name_series = pd.Series(np.random.choice(['adam', 'chang', 'eliza', 'odom'], replace=True, size=1000000))
apply
%timeit name_series.apply(parse_name)
결과:
769 ms ± 5.88 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
np.vectorize
%timeit parse_name_vec(name_series)
결과:
794 ms ± 4.85 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
보다 나은(벡터화된) 방법np.select:
cases = [
name_series.str.lower().str.startswith('a'), name_series.str.lower().str.startswith('e'),
name_series.str.lower().str.startswith('i'), name_series.str.lower().str.startswith('o'),
name_series.str.lower().str.startswith('u')
]
replacements = 'A E I O U'.split()
시간:
%timeit np.select(cases, replacements, default=name_series)
결과:
67.2 ms ± 683 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
언급URL : https://stackoverflow.com/questions/52673285/performance-of-pandas-apply-vs-np-vectorize-to-create-new-column-from-existing-c
'programing' 카테고리의 다른 글
| Oracle에서 데이터베이스 개체가 테이블 또는 보기인지 확인하는 방법 (0) | 2023.07.18 |
|---|---|
| 단순 문자열로 시간 델타 객체를 구성하는 방법 (0) | 2023.07.18 |
| 사용자 지정 데이터 주석 검증기를 만드는 방법 (0) | 2023.07.18 |
| 스프링 부트에서 프로필을 기반으로 선택적 테스트를 실행/해제하는 방법 (0) | 2023.07.18 |
| git log에서 지점 이름을 표시하려면 어떻게 해야 합니까? (0) | 2023.07.18 |