Pandas 데이터 프레임의 두 열에 함수를 적용하는 방법
의 df
열 이있는 것으로 가정하십시오 'ID', 'col_1', 'col_2'
. 그리고 함수를 정의합니다.
f = lambda x, y : my_function_expression
.
이제 f
to 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']
다음 apply
은 데이터 프레임에서 사용하는 예제 입니다 axis = 1
.
차이점은 함수에 두 개의 값을 전달하는 대신 f
pandas 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['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)
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]
흥미로운 질문입니다! 내 대답은 아래와 같습니다.
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
찾고있는 방법은 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]
당신이 쓴 방식에는 두 개의 입력이 필요합니다. 오류 메시지를 보면 f에 두 개의 입력을 제공하지 않고 하나만 제공한다고 표시됩니다. 오류 메시지가 정확합니다.
불일치는 df [[ 'col1', 'col2']]가 두 개의 개별 열이 아닌 두 개의 열이있는 단일 데이터 프레임을 반환하기 때문입니다.
단일 입력을 취하도록 f를 변경하고 위의 데이터 프레임을 입력으로 유지 한 다음 함수 본문 내 에서 x, y로 나눕니다 . 그런 다음 필요한 것을 수행하고 단일 값을 반환하십시오.
구문은 .apply (f)이므로이 함수 시그니처가 필요합니다. f는 현재 f가 기대하는 두 가지가 아니라 단일 것 = 데이터 프레임을 취해야합니다.
f의 본문을 제공하지 않았기 때문에 더 이상 도움을 줄 수는 없지만 기본적으로 코드를 변경하거나 적용하지 않고 다른 방법을 사용하지 않고 탈출구를 제공해야합니다.
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]
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)
적용하지 않고 문제에 대답
apply
axis = 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)
팬더 또는 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]
귀하의 질문에 대한 나의 예 :
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')
get_sublist
함수 를 변경하고 싶지 않고 DataFrame의 apply
메소드를 사용 하여 작업을 수행 하려고한다고 가정합니다 . 당신이 원하는 결과를 얻기 위하여는, 나는 두 도움말 기능을 작성했습니다 : get_sublist_list
와 unlist
. 함수 이름에서 알 수 있듯이 먼저 하위 목록을 가져오고 두 번째는 해당 목록에서 해당 하위 목록을 추출합니다. 마지막 으로이 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_sublist
get_sublist_list
ValueError: could not broadcast input array from shape (3) into shape (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)
'IT' 카테고리의 다른 글
Dockerfile에서 PATH 환경 변수를 업데이트하는 방법은 무엇입니까? (0) | 2020.03.25 |
---|---|
FOREIGN KEY 제약 조건을 도입하면 사이클 또는 여러 계단식 경로가 발생할 수 있습니다. 왜 그렇습니까? (0) | 2020.03.25 |
Node.JS : 명령 줄을 통해 요청을 통해 또는 직접 호출했는지 감지 (0) | 2020.03.25 |
대기와 수면의 차이점 (0) | 2020.03.25 |
Android 개발자 콘솔의 앱 목록에서 애플리케이션을 제거하는 방법 (0) | 2020.03.25 |