[python] Pandas : 기존 열에서 계산 된 값을 사용하여 데이터 프레임에 두 개의 새 열 만들기

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)


답변