Pandas 데이터 프레임의 두 열에 함수를 적용하는 방법


368

df열 이있는 것으로 가정하십시오 'ID', 'col_1', 'col_2'. 그리고 함수를 정의합니다.

f = lambda x, y : my_function_expression.

이제 fto df의 두 열 'col_1', 'col_2'을 요소별로 적용하여 새 열 을 요소별로 계산하려고합니다 'col_3'.

df['col_3'] = df[['col_1','col_2']].apply(f)  
# Pandas gives : TypeError: ('<lambda>() takes exactly 2 arguments (1 given)'

수행하는 방법 ?

** 아래와 같이 상세 샘플 추가 ***

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

  ID  col_1  col_2            col_3
0  1      0      1       ['a', 'b']
1  2      2      4  ['c', 'd', 'e']
2  3      3      5  ['d', 'e', 'f']

4
f를 열에 직접 적용 할 수 있습니까? df [ 'col_3'] = f (df [ 'col_1'], df [ 'col_2'])
btel

1
무엇 f을하고 있는지 아는 것이 도움이 될 것 입니다
tehmisvh

2
아니요, df [ 'col_3'] = f (df [ 'col_1'], df [ 'col_2'])가 작동하지 않습니다. f의 경우 벡터 입력이 아닌 스칼라 입력 만 허용합니다. 좋아, 당신은 f = lambda x, y : x + y라고 가정 할 수 있습니다. (물론, 내 실제 f는 그렇게 간단하지 않습니다. 그렇지 않으면 직접 df [ 'col_3'] = df [ 'col_1'] + df [ 'col_2'])
bigbug

1
url 아래에서 관련 Q & A를 찾았지만 문제는 1에서 2가 아닌 2 개의 기존 열로 새 열을 계산하는 것입니다. stackoverflow.com/questions/12356501/…
bigbug

내 답변 stackoverflow.com/a/52854800/5447172 는 해결 방법이나 숫자 인덱싱없이 가장 Pythonic / Pandanic 방식으로 이것에 대답 한다고 생각 합니다. 예제에서 필요한 출력을 정확하게 생성합니다.
ajrwhite

답변:


291

다음 apply은 데이터 프레임에서 사용하는 예제 입니다 axis = 1.

차이점은 두 값을 함수에 전달하는 대신 fpandas Series 객체를 허용하도록 함수를 다시 작성한 다음 Series를 인덱싱하여 필요한 값을 얻는다는 점입니다.

In [49]: df
Out[49]: 
          0         1
0  1.000000  0.000000
1 -0.494375  0.570994
2  1.000000  0.000000
3  1.876360 -0.229738
4  1.000000  0.000000

In [50]: def f(x):    
   ....:  return x[0] + x[1]  
   ....:  

In [51]: df.apply(f, axis=1) #passes a Series object, row-wise
Out[51]: 
0    1.000000
1    0.076619
2    1.000000
3    1.646622
4    1.000000

사용 사례에 따라 팬더 group객체를 만든 다음 apply그룹에서 사용 하는 것이 도움이되는 경우가 있습니다 .


예, 적용을 시도했지만 유효한 구문 표현식을 찾을 수 없습니다. 그리고 df의 각 행이 고유하다면 여전히 groupby를 사용합니까?
bigbug

내 대답에 예를 추가했습니다. 이것이 당신이 찾고있는 것을하기를 바랍니다. 그렇지 않은 경우 sum지금까지 제안 된 방법 중 하나를 통해 성공적으로 해결 되므로보다 구체적인 예제 기능을 제공하십시오 .
Aman

1
코드를 붙여 주시겠습니까? 함수를 다시 작성합니다. def get_sublist (x) : return mylist [x [1] : x [2] + 1] 및 df [ 'col_3'] = df.apply (get_sublist, axis = 1)은 'ValueError : operands could 셰이프와 함께 방송되지 않음 (2) (3) '
bigbug

3
@Aman : Pandas 버전 0.14.1 (및 이전 버전)에서는 람다 식도 사용할 수 있습니다. df정의한 객체를 제공하면 다른 결과 (동일한 결과)가 df.apply(lambda x: x[0] + x[1], axis = 1)있습니다.
Jubbles

2
@CanCeylan 인덱스 대신 함수에서 열 이름을 사용하면 순서 변경에 대해 걱정하거나 이름별로 인덱스를 얻을 필요가 없습니다. 예를 들어 stackoverflow.com/questions/13021654/…
Davos

165

Pandas에는 한 줄로 깔끔하게 정리할 수 있습니다.

df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)

이를 통해 f여러 입력 값을 가진 사용자 정의 함수가 가능하고 (안전하지 않은) 숫자 인덱스 대신 (안전한) 열 이름을 사용하여 열에 액세스합니다.

데이터가있는 예 (원래 질문을 기반으로 함) :

import pandas as pd

df = pd.DataFrame({'ID':['1', '2', '3'], 'col_1': [0, 2, 3], 'col_2':[1, 4, 5]})
mylist = ['a', 'b', 'c', 'd', 'e', 'f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

df['col_3'] = df.apply(lambda x: get_sublist(x.col_1, x.col_2), axis=1)

출력 print(df):

  ID  col_1  col_2      col_3
0  1      0      1     [a, b]
1  2      2      4  [c, d, e]
2  3      3      5  [d, e, f]

열 이름에 공백이 있거나 기존 데이터 프레임 속성과 이름을 공유하는 경우 대괄호로 색인을 생성 할 수 있습니다.

df['col_3'] = df.apply(lambda x: f(x['col 1'], x['col 2']), axis=1)

2
사용 axis=1하고 열을 호출 name하면 실제로 열 데이터가 아닌을 반환합니다 index. 에 들어가는 것과 비슷 name합니다 groupby(). 열 이름을 바꾸어이 문제를 해결했습니다.
Tom Hemmes 19

2
이거 야! 방금 여러 입력 매개 변수가있는 사용자 정의 함수를 람다에 삽입 할 수 있다는 것을 알지 못했습니다. Series.apply () 대신 DF.apply ()를 사용하고 있음을 명심해야합니다. 이를 통해 원하는 두 열을 사용하여 df를 색인화하고 전체 열을 함수에 전달할 수 있지만 apply ()를 사용하기 때문에 전체 열에 요소 방식으로 함수를 적용합니다. 훌륭한! 게시 해 주셔서 감사합니다!
Data-phile

1
드디어! 당신은 내 하루를 구했다!
Mysterio

제안 된 방법은 df.loc [:, 'new col'] = df.apply .....
valearner

@valearner 나는 .loc예제에서 선호 할 이유가 없다고 생각 합니다. 이것을 다른 문제 설정 (예 : 슬라이스 작업)에 적용하면 필요할 수 있습니다.
ajrwhite

86

간단한 해결책은 다음과 같습니다.

df['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)

1
이 답변이 질문의 접근 방식과 어떻게 다른가요? poster가이 axis = 1을 지정하지 않았습니다. 기본값은 axis = 0입니까?
Lost1

1
이 답변은 @Anman의 답변과 비슷하지만 약간 더 매끄 럽습니다. 그는 iterable을 가져 와서 함수 f에 전달하기 전에 압축을 푸는 익명 함수를 구성하고 있습니다.
tiao

39

흥미로운 질문입니다! 내 대답은 아래와 같습니다.

import pandas as pd

def sublst(row):
    return lst[row['J1']:row['J2']]

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(sublst,axis=1)
print df

산출:

  ID  J1  J2
0  1   0   1
1  2   2   4
2  3   3   5
  ID  J1  J2      J3
0  1   0   1     [a]
1  2   2   4  [c, d]
2  3   3   5  [d, e]

ID <J1 <J2 <J3을 보장하기 위해 열 이름을 ID, J1, J2, J3으로 변경하여 열이 올바른 순서로 표시됩니다.

하나 더 간단한 버전 :

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(lambda row:lst[row['J1']:row['J2']],axis=1)
print df

23

찾고있는 방법은 Series.combine입니다. 그러나 데이터 유형에 대해서는 약간의주의가 필요합니다. 귀하의 예에서 (응답을 테스트 할 때와 마찬가지로) 순진하게 전화하십시오.

df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)

그러나 이것은 오류를 발생시킵니다.

ValueError: setting an array element with a sequence.

가장 좋은 추측은 결과가 메소드를 호출하는 시리즈와 동일한 유형 일 것으로 예상되는 것 같습니다 (df.col_1 here). 그러나 다음과 같이 작동합니다.

df['col_3'] = df.col_1.astype(object).combine(df.col_2, func=get_sublist)

df

   ID   col_1   col_2   col_3
0   1   0   1   [a, b]
1   2   2   4   [c, d, e]
2   3   3   5   [d, e, f]

12

당신이 쓴 방식에는 두 개의 입력이 필요합니다. 오류 메시지를 보면 f에 두 개의 입력을 제공하지 않고 하나만 제공한다고 표시됩니다. 오류 메시지가 정확합니다.
불일치는 df [[ 'col1', 'col2']]가 두 개의 개별 열이 아닌 두 개의 열이있는 단일 데이터 프레임을 반환하기 때문입니다.

단일 입력을 취하도록 f를 변경하고 위의 데이터 프레임을 입력으로 유지 한 다음 함수 본문 에서 x, y로 나눕니다 . 그런 다음 필요한 것을 수행하고 단일 값을 반환하십시오.

구문은 .apply (f)이므로이 함수 서명이 필요합니다. f는 현재 f가 기대하는 두 가지가 아니라 단일 것 = 데이터 프레임을 가져와야합니다.

f의 본문을 제공하지 않았으므로 더 이상 도움을 줄 수는 없지만 기본적으로 코드를 변경하거나 적용하지 않고 다른 방법을 사용하지 않고 탈출구를 제공해야합니다.


12

np.vectorize에 대한 투표를하겠습니다. 그것은 x 개의 열을 넘어서서 함수의 데이터 프레임을 처리하지 않아도되므로 2 개의 열과 상수를 함수로 보내는 것과 같이 제어하거나 수행하지 않는 함수 (예 : col_1, col_2, 'foo').

import numpy as np
import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

df.loc[:,'col_3'] = np.vectorize(get_sublist, otypes=["O"]) (df['col_1'], df['col_2'])


df

ID  col_1   col_2   col_3
0   1   0   1   [a, b]
1   2   2   4   [c, d, e]
2   3   3   5   [d, e, f]

1
이것은 실제로 팬더를 사용하여 질문에 대답하지 않습니다.
mnky9800n

18
문제는 "Pandas 데이터 프레임의 두 열에 함수를 적용하는 방법"이 아니라 "Pandas 메서드 만 사용하여 Pandas 데이터 프레임의 두 열에 함수를 적용하는 방법"이며 numpy는 Pandas의 종속성이므로 어쨌든 설치해야합니다. 그래서 이것은 이상한 반대처럼 보입니다.
Trae Wallace

12

apply결과 개체가 Series 또는 DataFrame 중 하나라는 보장이 없으므로 목록을 반환하는 것은 위험한 작업입니다. 경우에 따라 예외가 발생할 수 있습니다. 간단한 예를 살펴 보겠습니다.

df = pd.DataFrame(data=np.random.randint(0, 5, (5,3)),
                  columns=['a', 'b', 'c'])
df
   a  b  c
0  4  0  0
1  2  0  1
2  2  2  2
3  1  2  2
4  3  0  0

에서 목록을 반환하면 가능한 세 가지 결과가 있습니다 apply

1) 리턴 된 목록의 길이가 열 수와 같지 않으면 일련의 목록이 리턴됩니다.

df.apply(lambda x: list(range(2)), axis=1)  # returns a Series
0    [0, 1]
1    [0, 1]
2    [0, 1]
3    [0, 1]
4    [0, 1]
dtype: object

2) 반환 된 목록의 길이가 열 수와 같으면 DataFrame이 반환되고 각 열은 목록의 해당 값을 가져옵니다.

df.apply(lambda x: list(range(3)), axis=1) # returns a DataFrame
   a  b  c
0  0  1  2
1  0  1  2
2  0  1  2
3  0  1  2
4  0  1  2

3) 반환 된 목록의 길이가 첫 번째 행의 열 수와 같지만 목록에 열 수와 다른 수의 요소가있는 행이 하나 이상있는 경우 ValueError가 발생합니다.

i = 0
def f(x):
    global i
    if i == 0:
        i += 1
        return list(range(3))
    return list(range(4))

df.apply(f, axis=1) 
ValueError: Shape of passed values is (5, 4), indices imply (5, 3)

적용하지 않고 문제에 대답

applyaxis = 1과 함께 사용하면 속도가 매우 느립니다. 기본 반복 방법으로 훨씬 더 나은 성능 (특히 더 큰 데이터 세트에서)을 얻을 수 있습니다.

더 큰 데이터 프레임 만들기

df1 = df.sample(100000, replace=True).reset_index(drop=True)

타이밍

# apply is slow with axis=1
%timeit df1.apply(lambda x: mylist[x['col_1']: x['col_2']+1], axis=1)
2.59 s ± 76.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# zip - similar to @Thomas
%timeit [mylist[v1:v2+1] for v1, v2 in zip(df1.col_1, df1.col_2)]  
29.5 ms ± 534 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

@ 토마스 답변

%timeit list(map(get_sublist, df1['col_1'],df1['col_2']))
34 ms ± 459 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

1
배울 수있는 곳에서 자세한 답변을 보는 것이 좋습니다.
Andrea Moro

7

팬더 또는 Numpy 작업을 사용하는 솔루션만큼 빠르지는 않지만 확실하게 함수를 다시 작성하지 않으려면 map을 사용할 수 있습니다. 원래 예제 데이터 사용-

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

df['col_3'] = list(map(get_sublist,df['col_1'],df['col_2']))
#In Python 2 don't convert above to list

이런 식으로 함수에 원하는만큼 많은 인수를 전달할 수 있습니다. 결과는 우리가 원하는 것입니다

ID  col_1  col_2      col_3
0  1      0      1     [a, b]
1  2      2      4  [c, d, e]
2  3      3      5  [d, e, f]

1
이것은 실제로 훨씬 더 빨리 그 답이 사용하는 것입니다 applyaxis=1
테드 페트로 우

2

귀하의 질문에 대한 나의 예 :

def get_sublist(row, col1, col2):
    return mylist[row[col1]:row[col2]+1]
df.apply(get_sublist, axis=1, col1='col_1', col2='col_2')

2

거대한 데이터 세트가있는 경우 더 빠르지 만 빠른 방법 으로이 작업을 수행 할 수 있습니다.

import pandas as pd
import swifter

def fnc(m,x,c):
    return m*x+c

df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})
df["y"] = df.swifter.apply(lambda x: fnc(x.m, x.x, x.c), axis=1)

1

get_sublist함수 를 변경하고 싶지 않고 DataFrame의 apply메소드를 사용 하여 작업을 수행 하려고한다고 가정합니다 . 당신이 원하는 결과를 얻기 위하여는, 나는 두 도움말 기능을 작성했습니다 : get_sublist_listunlist. 함수 이름에서 알 수 있듯이 먼저 하위 목록을 가져오고 두 번째는 해당 목록에서 해당 하위 목록을 추출합니다. 마지막 으로이 apply두 함수를 df[['col_1','col_2']]DataFrame 에 적용하려면 함수 를 호출해야합니다 .

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

def get_sublist_list(cols):
    return [get_sublist(cols[0],cols[1])]

def unlist(list_of_lists):
    return list_of_lists[0]

df['col_3'] = df[['col_1','col_2']].apply(get_sublist_list,axis=1).apply(unlist)

df

함수 []를 묶는 데 사용하지 않으면 함수는 일반 목록을 반환하며 @Ted Petrou가 언급했듯이을 올립니다 .get_sublistget_sublist_listValueError: could not broadcast input array from shape (3) into shape (2)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.