pandas 라이브러리로 작업하고 df
있으며 n 개의 열 (n> 0) 이있는 데이터 프레임에 두 개의 새 열을 추가하고 싶습니다 .
이러한 새 열은 데이터 프레임의 열 중 하나에 함수를 적용한 결과입니다.
적용 할 기능은 다음과 같습니다.
def calculate(x):
...operate...
return z, y
값만 반환하는 함수에 대한 새 열을 만드는 한 가지 방법은 다음과 같습니다.
df['new_col']) = df['column_A'].map(a_function)
그래서 내가 원하고 실패한 것 (*)은 다음과 같습니다.
(df['new_col_zetas'], df['new_col_ys']) = df['column_A'].map(calculate)
이를 달성하는 가장 좋은 방법은 무엇입니까? 단서없이 문서 를 스캔했습니다 .
** df['column_A'].map(calculate)
는 튜플 z, y로 구성된 각 항목에 판다 시리즈를 반환합니다. 그리고 이것을 두 개의 데이터 프레임 열에 할당하려고하면 ValueError가 발생합니다. *
답변
나는 그냥 사용합니다 zip
:
In [1]: from pandas import *
In [2]: def calculate(x):
...: return x*2, x*3
...:
In [3]: df = DataFrame({'a': [1,2,3], 'b': [2,3,4]})
In [4]: df
Out[4]:
a b
0 1 2
1 2 3
2 3 4
In [5]: df["A1"], df["A2"] = zip(*df["a"].map(calculate))
In [6]: df
Out[6]:
a b A1 A2
0 1 2 2 3
1 2 3 4 6
2 3 4 6 9
답변
내 의견으로는 최고의 답변이 잘못되었습니다. 바라건대, 아무도 .NET을 사용하여 모든 팬더를 네임 스페이스로 대량으로 가져 오지 않기를 바랍니다 from pandas import *
. 또한이 map
메서드는 사전 또는 Series를 전달할 때 해당 시간을 위해 예약되어야합니다. 그것은 기능을 취할 수 있지만 이것이 apply
사용되는 것입니다.
따라서 위의 접근 방식을 사용해야한다면 다음과 같이 작성합니다.
df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
실제로 여기에서 zip을 사용할 이유가 없습니다. 간단하게 다음과 같이 할 수 있습니다.
df["A1"], df["A2"] = calculate(df['a'])
이 두 번째 방법은 더 큰 DataFrame에서 훨씬 빠릅니다.
df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000})
300,000 개의 행으로 생성 된 DataFrame
%timeit df["A1"], df["A2"] = calculate(df['a'])
2.65 ms ± 92.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
159 ms ± 5.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
zip보다 60 배 빠름
일반적으로 적용을 사용하지 마십시오.
Apply는 일반적으로 Python 목록을 반복하는 것보다 빠르지 않습니다. 위와 동일한 작업을 수행하기 위해 for 루프의 성능을 테스트 해 보겠습니다.
%%timeit
A1, A2 = [], []
for val in df['a']:
A1.append(val**2)
A2.append(val**3)
df['A1'] = A1
df['A2'] = A2
298 ms ± 7.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
따라서 이것은 끔찍한 성능 회귀가 아닌 두 배 느리지 만 위의 것을 cythonize하면 훨씬 더 나은 성능을 얻습니다. ipython을 사용하고 있다고 가정합니다.
%load_ext cython
%%cython
cpdef power(vals):
A1, A2 = [], []
cdef double val
for val in vals:
A1.append(val**2)
A2.append(val**3)
return A1, A2
%timeit df['A1'], df['A2'] = power(df['a'])
72.7 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
적용하지 않고 직접 할당
직접 벡터화 된 작업을 사용하면 훨씬 더 빠른 속도 향상을 얻을 수 있습니다.
%timeit df['A1'], df['A2'] = df['a'] ** 2, df['a'] ** 3
5.13 ms ± 320 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
이것은 루프 대신 NumPy의 매우 빠른 벡터화 작업을 활용합니다. 이제 원본보다 30 배의 속도가 향상되었습니다.
가장 간단한 속도 테스트 apply
위의 예는 얼마나 느릴 apply
수 있는지 명확하게 보여 주어야 하지만 가장 기본적인 예를 살펴 보겠습니다. 적용 여부에 관계없이 일련의 천만 개의 숫자를 제곱합시다.
s = pd.Series(np.random.rand(10000000))
%timeit s.apply(calc)
3.3 s ± 57.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
적용하지 않으면 50 배 더 빠름
%timeit s ** 2
66 ms ± 2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)