팬더 DataFrame 또는 Series에 여러 필터를 적용하는 효율적인 방법
사용자가 Pandas DataFrame 또는 Series 객체에 여러 필터를 적용하려는 시나리오가 있습니다. 본질적으로 사용자가 런타임에 지정하는 여러 필터링 (비교 작업)을 효율적으로 연결하고 싶습니다.
필터는 부가 적이어야합니다 (일명 적용되는 각각의 결과는 좁아 져야 함).
현재 사용하고 reindex()있지만 매번 새 객체를 만들고 기본 데이터를 복사합니다 (문서를 올바르게 이해하면). 따라서 큰 Series 또는 DataFrame을 필터링 할 때 실제로 비효율적 일 수 있습니다.
내가 사용하는 것을 생각하고 apply(), map()또는 이와 유사한 일이 더 좋을 수 있습니다. 나는 여전히 팬더를 처음 접했지만 여전히 모든 것을 머리로 감싸려고합니다.
TL; DR
다음 형식의 사전을 가져 와서 각 작업을 주어진 Series 객체에 적용하고 '필터링 된'Series 객체를 반환하고 싶습니다.
relops = {'>=': [1], '<=': [1]}
긴 예
나는 현재 가지고있는 것의 예부터 시작하여 단일 Series 객체를 필터링합니다. 아래는 현재 사용중인 기능입니다.
def apply_relops(series, relops):
"""
Pass dictionary of relational operators to perform on given series object
"""
for op, vals in relops.iteritems():
op_func = ops[op]
for val in vals:
filtered = op_func(series, val)
series = series.reindex(series[filtered])
return series
사용자는 수행하려는 작업을 사전에 제공합니다.
>>> df = pandas.DataFrame({'col1': [0, 1, 2], 'col2': [10, 11, 12]})
>>> print df
>>> print df
col1 col2
0 0 10
1 1 11
2 2 12
>>> from operator import le, ge
>>> ops ={'>=': ge, '<=': le}
>>> apply_relops(df['col1'], {'>=': [1]})
col1
1 1
2 2
Name: col1
>>> apply_relops(df['col1'], relops = {'>=': [1], '<=': [1]})
col1
1 1
Name: col1
다시 말하지만, 위의 접근 방식의 '문제'는 중간 단계의 데이터를 불필요하게 복사 할 수 있다고 생각합니다.
또한 전달 된 사전에 연산자에 열을 포함하고 입력 사전을 기반으로 전체 DataFrame을 필터링 할 수 있도록 이것을 확장하고 싶습니다. 그러나 Series에 작동하는 모든 것을 DataFrame으로 쉽게 확장 할 수 있다고 가정합니다.
팬더 (및 numpy)는 부울 인덱싱을 허용 하므로 훨씬 효율적입니다.
In [11]: df.loc[df['col1'] >= 1, 'col1']
Out[11]:
1 1
2 2
Name: col1
In [12]: df[df['col1'] >= 1]
Out[12]:
col1 col2
1 1 11
2 2 12
In [13]: df[(df['col1'] >= 1) & (df['col1'] <=1 )]
Out[13]:
col1 col2
1 1 11
이를 위해 도우미 함수를 작성하려면 다음 행을 따라 무언가를 고려하십시오.
In [14]: def b(x, col, op, n):
return op(x[col],n)
In [15]: def f(x, *b):
return x[(np.logical_and(*b))]
In [16]: b1 = b(df, 'col1', ge, 1)
In [17]: b2 = b(df, 'col1', le, 1)
In [18]: f(df, b1, b2)
Out[18]:
col1 col2
1 1 11
업데이트 : pandas 0.13에는 이러한 종류의 사용 사례에 대한 쿼리 방법 이 있습니다. 열 이름이 다음 식별자의 유효한 식별자라고 가정하면 (큰 배후에서 numexpr 을 사용 하므로 큰 프레임에 더 효율적일 수 있습니다 ) :
In [21]: df.query('col1 <= 1 & 1 <= col1')
Out[21]:
col1 col2
1 1 11
연쇄 조건은 긴 줄을 생성하며, 이는 pep8에 의해 권장되지 않습니다. .query 메소드를 사용하면 문자열이 강력하게 사용되지만 강력하지는 않지만 강력하지는 않습니다.
각 필터가 설치되면 한 가지 접근 방식은
import numpy as np
import functools
def conjunction(*conditions):
return functools.reduce(np.logical_and, conditions)
c_1 = data.col1 == True
c_2 = data.col2 < 64
c_3 = data.col3 != 4
data_filtered = data[conjunction(c1,c2,c3)]
np.logical은 빠르게 작동하지만 functools.reduce에 의해 처리되는 두 개 이상의 인수를 사용하지 않습니다.
Note that this still has some redundancies: a) shortcutting does not happen on a global level b) Each of the individual conditions runs on the whole initial data. Still, I expect this to be efficient enough for many applications and it is very readable.
Simplest of All Solutions:
Use:
filtered_df = df[(df['col1'] >= 1) & (df['col1'] <= 5)]
Another Example, To filter the dataframe for values belonging to Feb-2018, use the below code
filtered_df = df[(df['year'] == 2018) & (df['month'] == 2)]
Since pandas 0.22 update, comparison options are available like:
- gt (greater than)
- lt (lesser than)
- eq (equals to)
- ne (not equals to)
- ge (greater than or equals to)
and many more. These functions return boolean array. Let's see how we can use them:
# sample data
df = pd.DataFrame({'col1': [0, 1, 2,3,4,5], 'col2': [10, 11, 12,13,14,15]})
# get values from col1 greater than or equals to 1
df.loc[df['col1'].ge(1),'col1']
1 1
2 2
3 3
4 4
5 5
# where co11 values is better 0 and 2
df.loc[df['col1'].between(0,2)]
col1 col2
0 0 10
1 1 11
2 2 12
# where col1 > 1
df.loc[df['col1'].gt(1)]
col1 col2
2 2 12
3 3 13
4 4 14
5 5 15
Why not do this?
def filt_spec(df, col, val, op):
import operator
ops = {'eq': operator.eq, 'neq': operator.ne, 'gt': operator.gt, 'ge': operator.ge, 'lt': operator.lt, 'le': operator.le}
return df[ops[op](df[col], val)]
pandas.DataFrame.filt_spec = filt_spec
Demo:
df = pd.DataFrame({'a': [1,2,3,4,5], 'b':[5,4,3,2,1]})
df.filt_spec('a', 2, 'ge')
Result:
a b
1 2 4
2 3 3
3 4 2
4 5 1
You can see that column 'a' has been filtered where a >=2.
This is slightly faster (typing time, not performance) than operator chaining. You could of course put the import at the top of the file.
e can also select rows based on values of a column that are not in a list or any iterable. We will create boolean variable just like before, but now we will negate the boolean variable by placing ~ in the front.
For example
list = [1, 0]
df[df.col1.isin(list)]
'programing tip' 카테고리의 다른 글
| 안전하지 않은 응답 또는 연결 거부로 인해 Ajax 호출이 실패했는지 판별 (0) | 2020.07.24 |
|---|---|
| C #에서는 디스크에서 쓰지 않고 문자열에서 TextReader 개체를 만드는 방법 (0) | 2020.07.23 |
| macOS에서 Anaconda를 완전히 제거하는 방법 (0) | 2020.07.23 |
| WPF 유효성 검사 오류 감지 (0) | 2020.07.23 |
| BOM (Byte Order Mark)없이 텍스트 파일을 작성 하시겠습니까? (0) | 2020.07.23 |