programing

Pandas에서 공식의 식을 동적으로 평가

lastcode 2023. 10. 9. 23:18
반응형

Pandas에서 공식의 식을 동적으로 평가

를 사용하여 열에 대한 .pd.eval 구체적으로 수식을 평가하는 다음 코드를 포팅하고 싶습니다.

x = 5
df2['D'] = df1['A'] + (df1['B'] * x)

을 .코드를 사용pd.eval.pd.eval많은 워크플로우를 자동화하고 싶으므로 동적으로 워크플로우를 생성하는 것이 저에게 유용합니다.

두 개의 입력 데이터 프레임은 다음과 같습니다.

import pandas as pd
import numpy as np

np.random.seed(0)
df1 = pd.DataFrame(np.random.choice(10, (5, 4)), columns=list('ABCD'))
df2 = pd.DataFrame(np.random.choice(10, (5, 4)), columns=list('ABCD'))

df1
   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6
3  8  8  1  6
4  7  7  8  1

df2
   A  B  C  D
0  5  9  8  9
1  4  3  0  3
2  5  0  2  3
3  8  1  3  3
4  3  7  0  1

는 를 더 잘 .pd.evalengine그리고.parser내 문제를 가장 잘 해결할 수 있는 방법을 결정하기 위한 논쟁.는 서류를 검토해 보았지만, 차이점이 명확하게 밝혀지지 않았습니다.

  1. 코드가 최대 성능으로 작동하는지 확인하려면 어떤 인수를 사용해야 합니까?
  2. 에 할 수 이 있습니까?df2?
  3. 을 더 , 를 x문자열 표현 안에 있는 논쟁으로?

1), 2) 또는 3)사용할 수 있습니다.그들의 다양한 특징과 기능에 대해서는 아래에서 설명합니다.

예를 들어, 별도로 지정하지 않는 한 이러한 데이터 프레임이 포함됩니다.

np.random.seed(0)
df1 = pd.DataFrame(np.random.choice(10, (5, 4)), columns=list('ABCD'))
df2 = pd.DataFrame(np.random.choice(10, (5, 4)), columns=list('ABCD'))
df3 = pd.DataFrame(np.random.choice(10, (5, 4)), columns=list('ABCD'))
df4 = pd.DataFrame(np.random.choice(10, (5, 4)), columns=list('ABCD'))

1) pandas.eval

이것은 팬더 문서가 포함해야 할 "실종 매뉴얼"입니다.참고: 논의 중인 세 가지 기능 중에서pd.eval가장 중요합니다.df.eval그리고.df.querypd.eval보닛 밑에동작과 사용은 세 가지 기능에 걸쳐 어느 정도 일치하며, 약간의 의미 변형은 나중에 강조될 것입니다.이 절에서는 허용되는 구문, 우선 순위 규칙키워드 인수를 포함하지만 이에 제한되지 않는 세 가지 함수 모두에서 공통된 기능을 소개합니다.

pd.eval변수 및/또는 리터럴로 구성될 수 있는 산술식을 평가할 수 있습니다.이 식을 문자열로 전달해야 합니다.그래서, 언급된 것처럼 질문에 대답하기 위해, 당신은 다음을 할 수 있습니다.

x = 5
pd.eval("df1.A + (df1.B * x)")

여기에 주의할 사항:

  1. 식 전체가 문자열입니다.
  2. df1,df2,그리고.x를 참조하며,이 변수들은됩니다.합니다.eval할 때
  3. 속성 접근자 색인을 사용하여 특정 열에 접근합니다.사용할 수도 있습니다."df1['A'] + (df1['B'] * x)"같은 취지로

는 에 .target=...아래의 속성입니다.합니다를 보겠습니다.pd.eval:

pd.eval("df1.A + df2.A")   # Valid, returns a pd.Series object
pd.eval("abs(df1) ** .5")  # Valid, returns a pd.DataFrame object

... 등등.조건식도 같은 방식으로 지원됩니다.아래 설명은 모두 유효한 표현이며 엔진이 평가합니다.

pd.eval("df1 > df2")
pd.eval("df1 > 5")
pd.eval("df1 < df2 and df3 < df4")
pd.eval("df1 in [1, 2, 3]")
pd.eval("1 < 2 < 3")

지원되는 모든 기능과 구문에 대한 자세한 목록은 설명서에서 확인할 수 있습니다.정리하자면,

  • ()<< 및 ()>> ) (), ,df + 2 * pi / s ** 4 % 42황금빛
  • 를 포함한 , ,2 < df < df2
  • , ,df < df2 and df3 < df4아니면not df_bool list그리고.tuple예를 들어, 문학작품.[1, 2]아니면(1, 2)
  • attribute access), (attribute access), :df.a
  • )(:df[0]
  • , ):pd.eval('df')(이는 그다지 유용하지 않습니다.
  • 수학함수: sin, cos, exp, log, expm1, log1p, sqrt, sinh, cosh, tanh, arcsin, arcos, arcsinh, arcsinh, arcsanh, abs 및 arcan2

이 의 이 에서는 합니다를 규칙을 합니다.set/dict리터럴, if-else 문, 루프 및 이해, 생성자 표현.

목록에서 다음과 같은 인덱스와 관련된 식을 전달할 수 있습니다.

pd.eval('df1.A * (df1.index > 1)')

1a) 파서 셀렉션: Theparser=...

pd.eval를 생성하기 구문 을 구문 할 때 두 다른 옵션인를 지원합니다.pandas그리고.python이 둘 차이점은 약간 이 둘 사이의 주요 차이점은 약간 다른 우선순위 규칙을 통해 강조됩니다.

하기 pandas, &그리고.|및된 AND및 OR입니다와 순위를 .and그리고.or.그렇게,

pd.eval("(df1 > df2) & (df3 < df4)")

다음과 같을 것입니다.

pd.eval("df1 > df2 & df3 < df4")
# pd.eval("df1 > df2 & df3 < df4", parser='pandas')

그리고 또한 마찬가지입니다.

pd.eval("df1 > df2 and df3 < df4")

여기서 괄호는 꼭 필요합니다.이를 수행하기 위해서는 괄호가 비트 와이즈 연산자의 높은 우선순위를 재정의해야 합니다.

(df1 > df2) & (df3 < df4)

그것 없이는 우리는

df1 > df2 & df3 < df4

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

사용하다parser='python'문자열을 평가하는 동안 python의 실제 연산자 우선 순위 규칙과 일관성을 유지하려면 다음과 같이 하십시오.

pd.eval("(df1 > df2) & (df3 < df4)", parser='python')

의 또 은 의 의미론입니다.==그리고.!=s와 있습니다.in그리고.not in할 때,때.'pandas'파서 예를 들면 예를들면,

pd.eval("df1 == [1, 2, 3]")

유효하며 다음과 같은 의미로 실행됩니다.

pd.eval("df1 in [1, 2, 3]")

OTOH,pd.eval("df1 == [1, 2, 3]", parser='python')NotImplementedError오류를 범실.

1b) 백엔드 셀렉션: Theengine=...

라는 두 가지 numexpr 및 값값)python.numexpr옵션은 성능에 최적화된 numexpr 백엔드를 사용합니다.

를 사용하면 하는 것과 eval기능. 과 같은 할 수 .예를 들어 문자열 연산과 같은 내부 표현을 유연하게 수행할 수 있습니다.

df = pd.DataFrame({'A': ['abc', 'def', 'abacus']})
pd.eval('df.A.str.contains("ab")', engine='python')

0     True
1    False
2     True
Name: A, dtype: bool

불행히도 이 방법은 다음과 같은 성능 이점을 제공하지 않습니다.numexpr엔진, 그리고 위험한 표현이 평가되지 않도록 보장하는 보안 조치는 매우 적으므로 자신의 위험무릅쓰고 사용하십시오!이 옵션은 일반적으로 다음으로 변경하지 않는 것이 좋습니다.'python'무슨 짓을 하는지 모르는 한 말입니다

1c)local_dict그리고.global_dict

식을 내부에 사용하지만 현재 네임스페이스에 정의되지 않은 변수 값을 제공하는 것이 유용한 경우도 있습니다.다에게 할 수 .local_dict

예를 들어,

pd.eval("df1 > thresh")

UndefinedVariableError: name 'thresh' is not defined

합니다. thresh정의되지 않았습니다.그러나 다음과 같이 작동합니다.

pd.eval("df1 > thresh", local_dict={'thresh': 10})

사전에서 제공할 변수가 있을 때 유용합니다.또는 Python 엔진을 사용하면 간단히 다음과 같은 작업을 수행할 수 있습니다.

mydict = {'thresh': 5}
# Dictionary values with *string* keys cannot be accessed without
# using the 'python' engine.
pd.eval('df1 > mydict["thresh"]', engine='python')

하지만 이것은 아마도 사용하는 것보다 훨씬 느릴 것입니다.'numexpr'local_dict아니면global_dict에 대한 있는 가 될 바라건대, 이것이 이러한 매개 변수의 사용에 대한 설득력 있는 논거가 되기를 바랍니다.

)target(+inplace및 )

이하는 더 간단한 방법이 에 이 사항이 되는 경우는 의 할 수 .pd.eval__getitem__를 들면sdict (, (하신) DataFrame

질문의 예를 생각해 보십시오.

x = 5
df2['D'] = df1['A'] + (df1['B'] * x)

를 "D"에df2, 우리가 합니다

pd.eval('D = df1.A + (df1.B * x)', target=df2)

   A  B  C   D
0  5  9  8   5
1  4  3  0  52
2  5  0  2  22
3  8  1  3  48
4  3  7  0  42

은 를 한 것이 아닙니다.df2(하지만 그럴 수도...계속 읽음).또 예로오를 해 보겠습니다

pd.eval('df1.A + df2.A')

0    10
1    11
2     7
3    16
4    10
dtype: int32

를 들어,하려면 , 를 DataFrame을 할 수 target다음과 같이 논합니다.

df = pd.DataFrame(columns=list('FBGH'), index=df1.index)
df
     F    B    G    H
0  NaN  NaN  NaN  NaN
1  NaN  NaN  NaN  NaN
2  NaN  NaN  NaN  NaN
3  NaN  NaN  NaN  NaN
4  NaN  NaN  NaN  NaN

df = pd.eval('B = df1.A + df2.A', target=df)
# Similar to
# df = df.assign(B=pd.eval('df1.A + df2.A'))

df
     F   B    G    H
0  NaN  10  NaN  NaN
1  NaN  11  NaN  NaN
2  NaN   7  NaN  NaN
3  NaN  16  NaN  NaN
4  NaN  10  NaN  NaN

df,inplace=True.

pd.eval('B = df1.A + df2.A', target=df, inplace=True)
# Similar to
# df['B'] = pd.eval('df1.A + df2.A')

df
     F   B    G    H
0  NaN  10  NaN  NaN
1  NaN  11  NaN  NaN
2  NaN   7  NaN  NaN
3  NaN  16  NaN  NaN
4  NaN  10  NaN  NaN

한다면inplace우, a 됩니다.ValueError인양됩니다.

에.target논쟁은 가지고 놀기에 재미있고, 당신은 그것을 거의 사용할 필요가 없을 것입니다.

으로 이걸 요.df.eval 합니다.

df = df.eval("B = @df1.A + @df2.A")
# df.eval("B = @df1.A + @df2.A", inplace=True)
df

     F   B    G    H
0  NaN  10  NaN  NaN
1  NaN  11  NaN  NaN
2  NaN   7  NaN  NaN
3  NaN  16  NaN  NaN
4  NaN  10  NaN  NaN

메모

는 ㅇpd.eval의하지 않은 을 의입니다와 분석하는 입니다.ast.literal_eval:

pd.eval("[1, 2, 3]")
array([1, 2, 3], dtype=object)

또한 다음과 같이 중첩된 목록을 구문 분석할 수 있습니다.'python'엔진:

pd.eval("[[1, 2, 3], [4, 5], [10]]", engine='python')
[[1, 2, 3], [4, 5], [10]]

문자열 목록:

pd.eval(["[1, 2, 3]", "[4, 5]", "[10]"], engine='python')
[[1, 2, 3], [4, 5], [10]]

그러나 문제는 길이가 100보다 큰 목록의 경우입니다.

pd.eval(["[1]"] * 100, engine='python') # Works
pd.eval(["[1]"] * 101, engine='python')

AttributeError: 'PandasExprVisitor' object has no attribute 'visit_Ellipsis'

이 오류의 원인, 해결 방법 및 해결 방법에 대한 자세한 내용은 여기에서 확인할 수 있습니다.


2) DataFrame.eval:

와 같이, ,df.evalpd.eval논쟁의 약간의 병치를 하고 있는 상태에서 말입니다.v0.23 소스 코드는 다음을 보여줍니다.

def eval(self, expr, inplace=False, **kwargs):

    from pandas.core.computation.eval import eval as _eval

    inplace = validate_bool_kwarg(inplace, 'inplace')
    resolvers = kwargs.pop('resolvers', None)
    kwargs['level'] = kwargs.pop('level', 0) + 1
    if resolvers is None:
        index_resolvers = self._get_index_resolvers()
        resolvers = dict(self.iteritems()), index_resolvers
    if 'target' not in kwargs:
        kwargs['target'] = self
    kwargs['resolvers'] = kwargs.get('resolvers', ()) + tuple(resolvers)
    return _eval(expr, inplace=inplace, **kwargs)

eval는 에 전달합니다.pd.eval.

자세한 내용은 DataFrame.eval() pandas.eval() 또는 Python eval()의 사용 시기를 참조하십시오.


2a) 사용상의 차이

2a1) 데이터 프레임을 사용한 표현 vs. 시리즈 표현식

DataFrame 를 .pd.eval를 들어, . , 와 한 값을 .pd.eval("df1 + df2")하실 때df1.eval아니면df2.eval.

2a2) 열 이름 지정

또 다른 주요 차이점은 열에 접근하는 방법입니다.를 들어 두 열 하려면 "A"와 "B"를열합니다.df1, 당신은 전화할 것입니다.pd.eval다음과 같은 표현으로

pd.eval("df1.A + df1.B")

df.eval의 경우 열 이름만 입력하면 됩니다.

df1.eval("A + B")

로의 맥락 에서.df1가 열 "A"가 "B" 합니다를 합니다.

를 할 수도 .index(인덱스 이름이 지정되지 않은 경우, 이 경우 이름을 사용합니다.)

df1.eval("A + index")

또는 일반적으로 인덱스가 1개 이상인 DataFrame의 경우 "index at level k"를 나타내는 변수 "ilevel_k"를 사용하여 식에서 인덱스의 kth 레벨을 참조할 수 있습니다.IOW, 위의 표현은 다음과 같이 쓸 수 있습니다.df1.eval("A + ilevel_0").

이 규칙들은 또한 다음에 적용됩니다.df.query.

2a3) 로컬/글로벌 네임스페이스의 변수 접근

열 이름과의 혼동을 방지하기 위해 식 내부에 제공된 변수는 "@" 기호 앞에 와야 합니다.

A = 5
df1.eval("A > @A")

입니다도 입니다.query.

입니다에서 수 지정 을 따라야 입니다.eval. 이름 지정 식별자에 대한 규칙 목록은 여기를 참조하십시오.

2a4) 다중질의할당

거의 eval 행 합니다()query그렇지 않습니다.를 들어, 몇 연산을 두의 새 열 "를 에 만든 F를으로 세 열 "를 어를 수행하고, df1열 "E"와 "F"고를으로하고, "E"와 "F"열 "G"다를 수 있습니다.

df1.eval("""
E = A + B
F = @df2.A + @df2.B
G = E >= F
""")

   A  B  C  D   E   F      G
0  5  0  3  3   5  14  False
1  7  9  3  5  16   7   True
2  2  4  7  6   6   5   True
3  8  8  1  6  16   9   True
4  7  7  8  1  14  10   True

3)eval.query

은 를 이 됩니다.df.querypd.eval서브루틴으로서

으로.query알 수 을 하고 이)예: True/False식)됩니다에 하는 데 됩니다.True결과. 식의 결과는 다음으로 전달됩니다에게 전달됩니다.loc(대부분의 경우) 식을 만족하는 행을 반환합니다.면,

의 평가 됩니다에게 됩니다.DataFrame.loc키 때문에 키예: DataFrame)다로 됩니다.DataFrame.__getitem__().

합니다를 합니다.pandas.eval()전달된 쿼리를 평가하는 함수입니다.

,query그리고.df.eval열 이름과 변수에 액세스하는 방법이 둘 다 비슷합니다.

위에서 언급한 바와 같이 이 둘의 주요 차이점은 표현 결과를 처리하는 방식입니다.이는 실제로 이 두 함수를 통해 식을 실행할 때 명확해집니다.예를 들어 다음을 생각해 보십시오.

df1.A

0    5
1    7
2    2
3    8
4    7
Name: A, dtype: int32

df1.B

0    9
1    3
2    0
3    1
4    7
Name: B, dtype: int32

"A" > = "B" 인 모든 행을 가져오려면 다음과 같이 하십시오.df1, 우리는 사용할 것입니다.eval다음과 같이:

m = df1.eval("A >= B")
m
0     True
1    False
2    False
3     True
4     True
dtype: bool

m는 A =B"를 식 "A = B"다입니다.그런 다음 마스크를 사용해 필터를 합니다.df1:

df1[m]
# df1.loc[m]

   A  B  C  D
0  5  0  3  3
3  8  8  1  6
4  7  7  8  1

과 .query은 "m" 로 됩니다.loc , .query, 당신은 단지 할 필요가 있을 것입니다.

df1.query("A >= B")

   A  B  C  D
0  5  0  3  3
3  8  8  1  6
4  7  7  8  1

성능 면에서는 똑같습니다.

df1_big = pd.concat([df1] * 100000, ignore_index=True)

%timeit df1_big[df1_big.eval("A >= B")]
%timeit df1_big.query("A >= B")

14.7 ms ± 33.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
14.7 ms ± 24.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

그러나 후자는 더 간결하고, 같은 동작을 한 단계로 표현합니다.

로 을 할 수도 하세요.query와 같이이어를 들어 df1.index하려면)

df1.query("index")
# Same as df1.loc[df1.index] # Pointless,... I know

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6
3  8  8  1  6
4  7  7  8  1

하지만 하지 마요.

을(를) 하십시오.query조건식을 기반으로 행을 쿼리하거나 필터링하는 경우.

이미 , 의 하시기 바랍니다.eval/query단순한 구문에 이끌려 데이터셋의 행 수가 15,000개 미만일 경우 심각한 성능 문제가 발생합니다.

그런 경우에는 사용만 하면 됩니다.df.loc[mask1, mask2].

참조: 평가를 통한평가()

Enter image description here

언급URL : https://stackoverflow.com/questions/53779986/dynamically-evaluate-an-expression-from-a-formula-in-pandas

반응형