팬더 : 연산자 체인을 사용하여 DataFrame의 행을 필터링하십시오.


329

에서 대부분의 작업 pandas운영자 체인 (함께 수행 할 수 있습니다 groupby, aggregate, apply, 등),하지만 난 필터 행을 찾은 유일한 방법은 일반 브라켓 색인을 통해입니다

df_filtered = df[df['column'] == value]

df변수 값을 필터링하기 전에 변수를 할당해야하므로이 방법이 적합하지 않습니다 . 다음과 같은 것이 더 있습니까?

df_filtered = df.mask(lambda x: x['column'] == value)

df.query그리고 pd.eval이 사용 사례에 대한 좋은 맞는 것처럼 보인다. pd.eval()함수 계열, 기능 및 사용 사례 에 대한 자세한 내용은 pd.eval ()을 사용하여 팬더의 Dynamic Expression Evaluation을 방문하십시오 .
cs95

답변:


384

나는 당신이 원하는 것을 완전히 확신하지 못하고, 마지막 코드 줄도 도움이되지 않지만 어쨌든 :

"체인"필터링은 부울 인덱스의 기준을 "체인"하여 수행됩니다.

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [99]: df[(df.A == 1) & (df.D == 6)]
Out[99]:
   A  B  C  D
d  1  3  9  6

메소드를 연결하려면 고유 한 마스크 메소드를 추가하고 사용할 수 있습니다.

In [90]: def mask(df, key, value):
   ....:     return df[df[key] == value]
   ....:

In [92]: pandas.DataFrame.mask = mask

In [93]: df = pandas.DataFrame(np.random.randint(0, 10, (4,4)), index=list('abcd'), columns=list('ABCD'))

In [95]: df.ix['d','A'] = df.ix['a', 'A']

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [97]: df.mask('A', 1)
Out[97]:
   A  B  C  D
a  1  4  9  1
d  1  3  9  6

In [98]: df.mask('A', 1).mask('D', 6)
Out[98]:
   A  B  C  D
d  1  3  9  6

2
좋은 대답입니다! 에서 (df.A == 1) & (df.D == 6)"&"가 Pandas의 오버로드 된 연산자입니까?
Shawn


그것은 정말 좋은 해결책입니다-파이썬에서와 같은 방법으로 배심원을 조작 할 수 있다는 것을 알지 못했습니다. 이와 같은 기능은 팬더 자체에 정말 좋습니다.
naught101

내가 가진 유일한 문제는의 사용입니다 pandas.. 당신은해야합니다 import pandas as pd.
Daisuke

3
실제로 import pandas as pd는 일반적인 관행입니다. 내가 질문에 대답했을 때가 의심 스럽다.
Wouter Overmeire

108

팬더 쿼리를 사용하여 필터를 연결할 수 있습니다 .

df = pd.DataFrame(np.random.randn(30, 3), columns=['a','b','c'])
df_filtered = df.query('a > 0').query('0 < b < 2')

단일 쿼리로 필터를 결합 할 수도 있습니다.

df_filtered = df.query('a > 0 and 0 < b < 2')

3
쿼리에서 파이썬 변수를 참조해야하는 경우 설명서 에 "@ a + b와 같은 '@'문자를 접두어로 붙여 환경의 변수를 참조 할 수 있습니다." 유효합니다 : df.query('a in list([1,2])'), s = set([1,2]); df.query('a in @s').
user3780389

2
반면, 열 이름에 특정 특수 문자 (예 : "Place.Name")가 있으면 쿼리 평가가 실패하는 것처럼 보입니다.
user3780389

2
연결은 쿼리를위한 것입니다.
piRSquared

66

@lodagro의 답변은 훌륭합니다. 마스크 기능을 다음과 같이 일반화하여 확장합니다.

def mask(df, f):
  return df[f(df)]

그런 다음 다음과 같은 작업을 수행 할 수 있습니다.

df.mask(lambda x: x[0] < 0).mask(lambda x: x[1] > 0)

8
유용한 일반화! 나는 그것이 DataFrame이미 S에 직접 통합되기를 바랍니다 !
duckworthd

24

이후 버전 0.18.1.loc 방법은 선택을위한 호출 수락. 람다 함수와 함께 매우 유연한 체인 가능 필터를 만들 수 있습니다.

import numpy as np
import pandas as pd

df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
df.loc[lambda df: df.A == 80]  # equivalent to df[df.A == 80] but chainable

df.sort_values('A').loc[lambda df: df.A > 80].loc[lambda df: df.B > df.A]

필터링 만하는 경우을 생략 할 수도 있습니다 .loc.


16

추가 예를 위해 이것을 제공합니다. 이것은 https://stackoverflow.com/a/28159296/ 과 같은 대답입니다.

이 게시물을 더 유용하게하기 위해 다른 수정 사항을 추가하겠습니다.

pandas.DataFrame.query
query정확히이 목적을 위해 만들어졌습니다. 데이터 프레임 고려df

import pandas as pd
import numpy as np

np.random.seed([3,1415])
df = pd.DataFrame(
    np.random.randint(10, size=(10, 5)),
    columns=list('ABCDE')
)

df

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

query모든 행을 필터링 하는 데 사용합시다 .D > B

df.query('D > B')

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

우리는 체인

df.query('D > B').query('C > B')
# equivalent to
# df.query('D > B and C > B')
# but defeats the purpose of demonstrating chaining

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

이것이 기본적으로 stackoverflow.com/a/28159296 과 같은 대답이 아닙니까? 대답 에서 명확하지 않은 것으로 생각되는 것이 있습니까?
bscan

9

기준을 OR 조건으로 결합하려는 것을 제외하고는 동일한 질문이있었습니다. Wouter Overmeire가 제공 한 형식은 기준을 AND 조건으로 결합하여 두 조건을 모두 충족해야합니다.

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [99]: df[(df.A == 1) & (df.D == 6)]
Out[99]:
   A  B  C  D
d  1  3  9  6

그러나 각 조건을 래핑 (... == True)하고 파이프와 조건을 결합하면 조건이 OR 조건으로 결합되어 조건이 충족 될 때마다 충족됩니다.

df[((df.A==1) == True) | ((df.D==6) == True)]

12
df[(df.A==1) | (df.D==6)]달성하려는 것에 충분 하지 않습니까?
eenblam 2016 년

아니요, 조건을 만족하는 모든 데이터를 필터링하는 것보다 볼 리언 결과 (True vs False)가 아니기 때문에 그렇지 않습니다. 내가 분명히하기를 바랍니다.
MGB.py 2014

8

팬더는 Wouter Overmeire의 답변에 두 가지 대안을 제공하며 재정의가 필요하지 않습니다. 하나는 .loc[.]호출 가능

df_filtered = df.loc[lambda x: x['column'] == value]

다른 쪽 .pipe(), 마찬가지로

df_filtered = df.pipe(lambda x: x['column'] == value)

7

내 대답은 다른 사람과 비슷합니다. 새 기능을 작성하지 않으려면 팬더가 이미 정의한 것을 사용할 수 있습니다. 파이프 방법을 사용하십시오.

df.pipe(lambda d: d[d['column'] == value])

이건 당신이 체인 명령을하려는 경우와 같은 원하는 것입니다a.join(b).pipe(lambda df: df[df.column_to_filter == 'VALUE'])
표시 이름

4

범용 마스크뿐만 아니라 모든 공통 부울 마스크를 적용하려면 파일에서 다음을 척킹하고 다음과 같이 간단히 모두 할당 할 수 있습니다.

pd.DataFrame = apply_masks()

용법:

A = pd.DataFrame(np.random.randn(4, 4), columns=["A", "B", "C", "D"])
A.le_mask("A", 0.7).ge_mask("B", 0.2)... (May be repeated as necessary

약간 해 키지 만 필터에 따라 데이터 세트를 지속적으로 자르고 변경하는 경우 일을 조금 더 깨끗하게 만들 수 있습니다. gen_mask 함수에는 Daniel Velkov의 범용 필터가 있으며 람다 함수 또는 원하는 경우 사용할 수 있습니다.

저장할 파일 (masks.py를 사용합니다) :

import pandas as pd

def eq_mask(df, key, value):
    return df[df[key] == value]

def ge_mask(df, key, value):
    return df[df[key] >= value]

def gt_mask(df, key, value):
    return df[df[key] > value]

def le_mask(df, key, value):
    return df[df[key] <= value]

def lt_mask(df, key, value):
    return df[df[key] < value]

def ne_mask(df, key, value):
    return df[df[key] != value]

def gen_mask(df, f):
    return df[f(df)]

def apply_masks():

    pd.DataFrame.eq_mask = eq_mask
    pd.DataFrame.ge_mask = ge_mask
    pd.DataFrame.gt_mask = gt_mask
    pd.DataFrame.le_mask = le_mask
    pd.DataFrame.lt_mask = lt_mask
    pd.DataFrame.ne_mask = ne_mask
    pd.DataFrame.gen_mask = gen_mask

    return pd.DataFrame

if __name__ == '__main__':
    pass

3

이 솔루션은 구현 측면에서 더 해킹 적이지만 사용법 측면에서 훨씬 깨끗하며 다른 솔루션보다 더 일반적입니다.

https://github.com/toobaz/generic_utils/blob/master/generic_utils/pandas/where.py

전체 저장소를 다운로드 할 필요가 없습니다 : 파일 저장 및 수행

from where import where as W

충분해야합니다. 그런 다음 다음과 같이 사용하십시오.

df = pd.DataFrame([[1, 2, True],
                   [3, 4, False], 
                   [5, 7, True]],
                  index=range(3), columns=['a', 'b', 'c'])
# On specific column:
print(df.loc[W['a'] > 2])
print(df.loc[-W['a'] == W['b']])
print(df.loc[~W['c']])
# On entire - or subset of a - DataFrame:
print(df.loc[W.sum(axis=1) > 3])
print(df.loc[W[['a', 'b']].diff(axis=1)['b'] > 1])

약간 덜 어리석은 사용법 예제 :

data = pd.read_csv('ugly_db.csv').loc[~(W == '$null$').any(axis=1)]

그건 그렇고 : 부울 열을 사용하는 경우에도,

df.loc[W['cond1']].loc[W['cond2']]

보다 더 효율적일 수있다

df.loc[W['cond1'] & W['cond2']]

이 평가되므로 cond2경우에만 cond1입니다True .

면책 조항 : 나는 이것을 보지 못했기 때문에이 답변을 다른 곳에서 처음으로 주었다 .


2

다음을 사용하여 데모를 추가하고 싶습니다. loc행 단위뿐만 아니라 열 및 일부 작업을 필터링하여 연쇄 연산 .

아래 코드는 값으로 행을 필터링 할 수 있습니다.

df_filtered = df.loc[df['column'] == value]

약간 수정하면 열도 필터링 할 수 있습니다.

df_filtered = df.loc[df['column'] == value, ['year', 'column']]

그렇다면 왜 우리는 연결 방법을 원합니까? 답은 조작이 많은 경우 읽기 쉽다는 것입니다. 예를 들어

res =  df\
    .loc[df['station']=='USA', ['TEMP', 'RF']]\
    .groupby('year')\
    .agg(np.nanmean)

2

df변수 값을 필터링하기 전에 변수를 할당해야하므로이 방법이 적합하지 않습니다 .

df[df["column_name"] != 5].groupby("other_column_name")

작동하는 것 같습니다 : []연산자를 중첩시킬 수도 있습니다. 당신이 질문을 한 이후에 그들이 추가했을 수도 있습니다.


1
df이제 체인 에서 이전 부분의 출력을 반드시 참조 할 필요는 없기 때문에 이것은 체인에서 거의 의미 가 없습니다.
Daan Luttik 2016 년

@DaanLuttik : 동의했지만 체인이 아니라 중첩입니다. 너에게 더 좋은?
serv-inc

1

열을 색인으로 검색하도록 설정 DataFrame.xs()하면 단면을 사용할 수 있습니다. 이것은 query답변 만큼 다재다능하지 않지만 일부 상황에서는 유용 할 수 있습니다.

import pandas as pd
import numpy as np

np.random.seed([3,1415])
df = pd.DataFrame(
    np.random.randint(3, size=(10, 5)),
    columns=list('ABCDE')
)

df
# Out[55]: 
#    A  B  C  D  E
# 0  0  2  2  2  2
# 1  1  1  2  0  2
# 2  0  2  0  0  2
# 3  0  2  2  0  1
# 4  0  1  1  2  0
# 5  0  0  0  1  2
# 6  1  0  1  1  1
# 7  0  0  2  0  2
# 8  2  2  2  2  2
# 9  1  2  0  2  1

df.set_index(['A', 'D']).xs([0, 2]).reset_index()
# Out[57]: 
#    A  D  B  C  E
# 0  0  2  2  2  2
# 1  0  2  1  1  0

1

논리 연산을 위해 numpy 라이브러리를 활용할 수도 있습니다. 꽤 빠릅니다.

df[np.logical_and(df['A'] == 1 ,df['B'] == 6)]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.